diff options
262 files changed, 6495 insertions, 2001 deletions
@@ -1,5 +1,12 @@ package(default_visibility = ["//visibility:public"]) +config_setting( + name = "jdk9", + values = { + "java_toolchain": "@bazel_tools//tools/jdk:toolchain_jdk9", + }, +) + genrule( name = "all", testonly = 1, @@ -16,6 +16,12 @@ maven_jar( ) maven_jar( + name = "jzlib", + artifact = "com.jcraft:jzlib:1.1.1", + sha1 = "a1551373315ffc2f96130a0e5704f74e151777ba", +) + +maven_jar( name = "javaewah", artifact = "com.googlecode.javaewah:JavaEWAH:1.1.6", sha1 = "94ad16d728b374d65bd897625f3fbb3da223a2b6", @@ -65,14 +71,14 @@ maven_jar( maven_jar( name = "commons_compress", - artifact = "org.apache.commons:commons-compress:1.6", - sha1 = "c7d9b580aff9e9f1998361f16578e63e5c064699", + artifact = "org.apache.commons:commons-compress:1.15", + sha1 = "b686cd04abaef1ea7bc5e143c080563668eec17e", ) maven_jar( name = "tukaani_xz", - artifact = "org.tukaani:xz:1.3", - sha1 = "66db21c8484120cb6a51b5b3ea47b6f383942bec", + artifact = "org.tukaani:xz:1.6", + sha1 = "05b6f921f1810bdf90e25471968f741f87168b64", ) maven_jar( @@ -101,8 +107,8 @@ maven_jar( maven_jar( name = "gson", - artifact = "com.google.code.gson:gson:2.2.4", - sha1 = "a60a5e993c98c864010053cb901b7eab25306568", + artifact = "com.google.code.gson:gson:2.8.2", + sha1 = "3edcfe49d2c6053a70a2a47e4e1c2f94998a49cf", ) JETTY_VER = "9.4.8.v20171121" @@ -30,7 +30,10 @@ java_library( java_library( name = "gson", - visibility = ["//org.eclipse.jgit.lfs.server:__pkg__"], + visibility = [ + "//org.eclipse.jgit.lfs:__pkg__", + "//org.eclipse.jgit.lfs.server:__pkg__", + ], exports = ["@gson//jar"], ) @@ -114,6 +117,15 @@ java_library( ) java_library( + name = "jzlib", + visibility = [ + "//org.eclipse.jgit:__pkg__", + "//org.eclipse.jgit.test:__pkg__", + ], + exports = ["@jzlib//jar"], +) + +java_library( name = "junit", testonly = 1, visibility = ["//visibility:public"], diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs index 64f74989e1..794592dee1 100644 --- a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF index f0f7712c6a..01577ae5b0 100644 --- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF @@ -4,13 +4,13 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.ant.test Bundle-SymbolicName: org.eclipse.jgit.ant.test -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.ant.tasks;version="[4.10.1,4.11.0)", - org.eclipse.jgit.junit;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)", + org.eclipse.jgit.ant.tasks;version="[4.11.2,4.12.0)", + org.eclipse.jgit.junit;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)", org.hamcrest.core;version="[1.1.0,2.0.0)", org.junit;version="[4.12,5.0.0)" diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml index 1386a269e9..c90167395a 100644 --- a/org.eclipse.jgit.ant.test/pom.xml +++ b/org.eclipse.jgit.ant.test/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ant.test</artifactId> diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs index 4d260cf480..565b75c658 100644 --- a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF index a056886ffc..d148477042 100644 --- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF @@ -3,11 +3,11 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.ant Bundle-SymbolicName: org.eclipse.jgit.ant -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.storage.file;version="[4.10.1,4.11.0)" + org.eclipse.jgit.storage.file;version="[4.11.2,4.12.0)" Bundle-Localization: plugin Bundle-Vendor: %Provider-Name -Export-Package: org.eclipse.jgit.ant.tasks;version="4.10.1"; +Export-Package: org.eclipse.jgit.ant.tasks;version="4.11.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 56dc17be29..29421d0ad0 100644 --- a/org.eclipse.jgit.ant/pom.xml +++ b/org.eclipse.jgit.ant/pom.xml @@ -48,7 +48,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ant</artifactId> diff --git a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs index 06ddbabb48..13c32a6d94 100644 --- a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF index de627aa88c..2fb90304bf 100644 --- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.archive Bundle-SymbolicName: org.eclipse.jgit.archive -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.8 @@ -13,15 +13,15 @@ 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.10.1,4.11.0)", - org.eclipse.jgit.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.nls;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)", + org.eclipse.jgit.api;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.nls;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)", org.osgi.framework;version="[1.3.0,2.0.0)" Bundle-ActivationPolicy: lazy Bundle-Activator: org.eclipse.jgit.archive.FormatActivator -Export-Package: org.eclipse.jgit.archive;version="4.10.1"; +Export-Package: org.eclipse.jgit.archive;version="4.11.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 6c43e29b2d..886a18091c 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.10.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.10.1.qualifier";roots="." +Bundle-Version: 4.11.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.11.2.qualifier";roots="." diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml index fde3f5172e..4e3a56eb0c 100644 --- a/org.eclipse.jgit.archive/pom.xml +++ b/org.eclipse.jgit.archive/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.archive</artifactId> diff --git a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs index 4d260cf480..565b75c658 100644 --- a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF index 6da3208be2..1af70ce581 100644 --- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.http.apache Bundle-SymbolicName: org.eclipse.jgit.http.apache -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-Localization: plugin Bundle-Vendor: %Provider-Name @@ -23,10 +23,10 @@ Import-Package: org.apache.http;version="[4.3.0,5.0.0)", org.apache.http.impl.client;version="[4.3.0,5.0.0)", org.apache.http.impl.conn;version="[4.3.0,5.0.0)", org.apache.http.params;version="[4.3.0,5.0.0)", - org.eclipse.jgit.nls;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport.http;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)" -Export-Package: org.eclipse.jgit.transport.http.apache;version="4.10.1"; + org.eclipse.jgit.nls;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.http;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)" +Export-Package: org.eclipse.jgit.transport.http.apache;version="4.11.2"; uses:="org.apache.http.client, org.eclipse.jgit.transport.http, org.apache.http.entity, diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml index b3bcc509be..bda1bf1063 100644 --- a/org.eclipse.jgit.http.apache/pom.xml +++ b/org.eclipse.jgit.http.apache/pom.xml @@ -48,7 +48,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.apache</artifactId> diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs index 4d260cf480..565b75c658 100644 --- a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF index e8502359c1..b89e37ce1a 100644 --- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF @@ -3,13 +3,13 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.http.server Bundle-SymbolicName: org.eclipse.jgit.http.server -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name -Export-Package: org.eclipse.jgit.http.server;version="4.10.1", - org.eclipse.jgit.http.server.glue;version="4.10.1"; +Export-Package: org.eclipse.jgit.http.server;version="4.11.2", + org.eclipse.jgit.http.server.glue;version="4.11.2"; uses:="javax.servlet,javax.servlet.http", - org.eclipse.jgit.http.server.resolver;version="4.10.1"; + org.eclipse.jgit.http.server.resolver;version="4.11.2"; uses:="org.eclipse.jgit.transport.resolver, org.eclipse.jgit.lib, org.eclipse.jgit.transport, @@ -18,12 +18,12 @@ Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: javax.servlet;version="[2.5.0,3.2.0)", javax.servlet.http;version="[2.5.0,3.2.0)", - org.eclipse.jgit.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.dfs;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.nls;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport.resolver;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)" + org.eclipse.jgit.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.dfs;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.nls;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.resolver;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)" diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml index 454d8080cd..7cc318d64c 100644 --- a/org.eclipse.jgit.http.server/pom.xml +++ b/org.eclipse.jgit.http.server/pom.xml @@ -52,7 +52,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.server</artifactId> diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java index 91e749e046..0d935fc98b 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java @@ -137,8 +137,7 @@ final class FileSender { rsp.setHeader(HDR_CONTENT_LENGTH, Long.toString(end - pos)); if (sendBody) { - final OutputStream out = rsp.getOutputStream(); - try { + try (OutputStream out = rsp.getOutputStream()) { final byte[] buf = new byte[4096]; source.seek(pos); while (pos < end) { @@ -151,8 +150,6 @@ final class FileSender { pos += n; } out.flush(); - } finally { - out.close(); } } } diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java index d6955b4a58..b9ca12bf3d 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java @@ -314,11 +314,8 @@ public class GitSmartHttpTools { res.setStatus(HttpServletResponse.SC_OK); res.setContentType(type); res.setContentLength(buf.length); - OutputStream os = res.getOutputStream(); - try { + try (OutputStream os = res.getOutputStream()) { os.write(buf); - } finally { - os.close(); } } diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java index ccb76adca0..4f70cf7004 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java @@ -74,29 +74,30 @@ class InfoRefsServlet extends HttpServlet { rsp.setCharacterEncoding(Constants.CHARACTER_ENCODING); final Repository db = getRepository(req); - final OutputStreamWriter out = new OutputStreamWriter( + try (OutputStreamWriter out = new OutputStreamWriter( new SmartOutputStream(req, rsp, true), - Constants.CHARSET); - final RefAdvertiser adv = new RefAdvertiser() { - @Override - protected void writeOne(final CharSequence line) throws IOException { - // Whoever decided that info/refs should use a different - // delimiter than the native git:// protocol shouldn't - // be allowed to design this sort of stuff. :-( - out.append(line.toString().replace(' ', '\t')); - } + Constants.CHARSET)) { + final RefAdvertiser adv = new RefAdvertiser() { + @Override + protected void writeOne(final CharSequence line) + throws IOException { + // Whoever decided that info/refs should use a different + // delimiter than the native git:// protocol shouldn't + // be allowed to design this sort of stuff. :-( + out.append(line.toString().replace(' ', '\t')); + } - @Override - protected void end() { - // No end marker required for info/refs format. - } - }; - adv.init(db); - adv.setDerefTags(true); + @Override + protected void end() { + // No end marker required for info/refs format. + } + }; + adv.init(db); + adv.setDerefTags(true); - Map<String, Ref> refs = db.getRefDatabase().getRefs(ALL); - refs.remove(Constants.HEAD); - adv.send(refs); - out.close(); + Map<String, Ref> refs = db.getRefDatabase().getRefs(ALL); + refs.remove(Constants.HEAD); + adv.send(refs); + } } } diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs index 64f74989e1..794592dee1 100644 --- a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF index a7f36679e0..8012f272cb 100644 --- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.http.test Bundle-SymbolicName: org.eclipse.jgit.http.test -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.8 @@ -25,25 +25,25 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.0)", org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)", - org.eclipse.jgit.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.http.server;version="[4.10.1,4.11.0)", - org.eclipse.jgit.http.server.glue;version="[4.10.1,4.11.0)", - org.eclipse.jgit.http.server.resolver;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.dfs;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.reftable;version="[4.10.1,4.11.0)", - org.eclipse.jgit.junit;version="[4.10.1,4.11.0)", - org.eclipse.jgit.junit.http;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.nls;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport.http;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport.http.apache;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport.resolver;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)", + org.eclipse.jgit.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.http.server;version="[4.11.2,4.12.0)", + org.eclipse.jgit.http.server.glue;version="[4.11.2,4.12.0)", + org.eclipse.jgit.http.server.resolver;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.dfs;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.reftable;version="[4.11.2,4.12.0)", + org.eclipse.jgit.junit;version="[4.11.2,4.12.0)", + org.eclipse.jgit.junit.http;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.nls;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.http;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.http.apache;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.resolver;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)", org.hamcrest.core;version="[1.1.0,2.0.0)", org.junit;version="[4.12,5.0.0)", org.junit.runner;version="[4.12,5.0.0)", diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml index 81a7610a08..e8bbac58c7 100644 --- a/org.eclipse.jgit.http.test/pom.xml +++ b/org.eclipse.jgit.http.test/pom.xml @@ -51,7 +51,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.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/AdvertiseErrorTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java index 0e92b1460c..5a46967766 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java @@ -135,8 +135,7 @@ public class AdvertiseErrorTest extends HttpTestCase { final RevCommit Q = src.commit().add("Q", Q_txt).create(); final Repository db = src.getRepository(); final String dstName = Constants.R_HEADS + "new.branch"; - final Transport t = Transport.open(db, remoteURI); - try { + try (Transport t = Transport.open(db, remoteURI)) { final String srcExpr = Q.name(); final boolean forceUpdate = false; final String localName = null; @@ -154,8 +153,6 @@ public class AdvertiseErrorTest extends HttpTestCase { + "come back next year!", // error.getMessage()); } - } finally { - t.close(); } } } diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java index 727f9bab00..4ff81c5474 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java @@ -140,9 +140,8 @@ public class DumbClientSmartServerTest extends HttpTestCase { assertEquals("http", remoteURI.getScheme()); Map<String, Ref> map; - Transport t = Transport.open(dst, remoteURI); + try (Transport t = Transport.open(dst, remoteURI)) { ((TransportHttp) t).setUseSmartHttp(false); - try { // I didn't make up these public interface names, I just // approved them for inclusion into the code base. Sorry. // --spearce @@ -150,14 +149,9 @@ public class DumbClientSmartServerTest extends HttpTestCase { assertTrue("isa TransportHttp", t instanceof TransportHttp); assertTrue("isa HttpTransport", t instanceof HttpTransport); - FetchConnection c = t.openFetch(); - try { + try (FetchConnection c = t.openFetch()) { map = c.getRefsMap(); - } finally { - c.close(); } - } finally { - t.close(); } assertNotNull("have map of refs", map); @@ -201,12 +195,9 @@ public class DumbClientSmartServerTest extends HttpTestCase { Repository dst = createBareRepository(); assertFalse(dst.hasObject(A_txt)); - Transport t = Transport.open(dst, remoteURI); + try (Transport t = Transport.open(dst, remoteURI)) { ((TransportHttp) t).setUseSmartHttp(false); - try { t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); - } finally { - t.close(); } assertTrue(dst.hasObject(A_txt)); @@ -229,12 +220,9 @@ public class DumbClientSmartServerTest extends HttpTestCase { Repository dst = createBareRepository(); assertFalse(dst.hasObject(A_txt)); - Transport t = Transport.open(dst, remoteURI); - ((TransportHttp) t).setUseSmartHttp(false); - try { + try (Transport t = Transport.open(dst, remoteURI)) { + ((TransportHttp) t).setUseSmartHttp(false); t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); - } finally { - t.close(); } assertTrue(dst.hasObject(A_txt)); @@ -265,9 +253,8 @@ public class DumbClientSmartServerTest extends HttpTestCase { final RevCommit Q = src.commit().create(); final Repository db = src.getRepository(); - Transport t = Transport.open(db, remoteURI); - ((TransportHttp) t).setUseSmartHttp(false); - try { + try (Transport t = Transport.open(db, remoteURI)) { + ((TransportHttp) t).setUseSmartHttp(false); try { t.push(NullProgressMonitor.INSTANCE, push(src, Q)); fail("push incorrectly completed against a smart server"); @@ -275,8 +262,6 @@ public class DumbClientSmartServerTest extends HttpTestCase { String exp = "smart HTTP push disabled"; assertEquals(exp, nse.getMessage()); } - } finally { - t.close(); } } } diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java index de7891c445..8dce98beef 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java @@ -176,7 +176,6 @@ public class GitServletResponseTests extends HttpTestCase { final RevCommit Q = client.commit().add("Q", Q_txt).create(); final Repository clientRepo = client.getRepository(); final String srvBranchName = Constants.R_HEADS + "new.branch"; - Transport t; maxPackSize = 0; postHook = null; @@ -188,8 +187,7 @@ public class GitServletResponseTests extends HttpTestCase { } }; - t = Transport.open(clientRepo, srvURI); - try { + try (Transport t = Transport.open(clientRepo, srvURI)) { RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(), srvBranchName, false, null, null); try { @@ -199,8 +197,6 @@ public class GitServletResponseTests extends HttpTestCase { } catch (Exception e) { assertTrue(e instanceof TransportException); } - } finally { - t.close(); } } @@ -218,7 +214,6 @@ public class GitServletResponseTests extends HttpTestCase { final RevCommit Q = client.commit().add("Q", Q_txt).create(); final Repository clientRepo = client.getRepository(); final String srvBranchName = Constants.R_HEADS + "new.branch"; - Transport t; maxPackSize = 0; postHook = null; @@ -231,8 +226,7 @@ public class GitServletResponseTests extends HttpTestCase { } }; - t = Transport.open(clientRepo, srvURI); - try { + try (Transport t = Transport.open(clientRepo, srvURI)) { RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(), srvBranchName, false, null, null); try { @@ -242,8 +236,6 @@ public class GitServletResponseTests extends HttpTestCase { } catch (Exception e) { assertTrue(e instanceof TransportException); } - } finally { - t.close(); } } @@ -266,7 +258,6 @@ public class GitServletResponseTests extends HttpTestCase { final RevCommit Q = client.commit().add("Q", Q_txt).create(); final Repository clientRepo = client.getRepository(); final String srvBranchName = Constants.R_HEADS + "new.branch"; - Transport t; // this maxPackSize leads to an unPackError maxPackSize = 100; @@ -283,8 +274,7 @@ public class GitServletResponseTests extends HttpTestCase { } }; - t = Transport.open(clientRepo, srvURI); - try { + try (Transport t = Transport.open(clientRepo, srvURI)) { RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(), srvBranchName, false, null, null); try { @@ -294,8 +284,6 @@ public class GitServletResponseTests extends HttpTestCase { } catch (Exception e) { assertTrue(e instanceof TooLargePackException); } - } finally { - t.close(); } } } diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java index adb69ec279..7795658eac 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java @@ -149,11 +149,9 @@ public class HookMessageTest extends HttpTestCase { final RevCommit Q = src.commit().add("Q", Q_txt).create(); final Repository db = src.getRepository(); final String dstName = Constants.R_HEADS + "new.branch"; - Transport t; PushResult result; - t = Transport.open(db, remoteURI); - try { + try (Transport t = Transport.open(db, remoteURI)) { final String srcExpr = Q.name(); final boolean forceUpdate = false; final String localName = null; @@ -163,8 +161,6 @@ public class HookMessageTest extends HttpTestCase { srcExpr, dstName, forceUpdate, localName, oldId); result = t.push(NullProgressMonitor.INSTANCE, Collections .singleton(update)); - } finally { - t.close(); } assertTrue(remoteRepository.hasObject(Q_txt)); @@ -193,12 +189,10 @@ public class HookMessageTest extends HttpTestCase { final RevCommit Q = src.commit().add("Q", Q_txt).create(); final Repository db = src.getRepository(); final String dstName = Constants.R_HEADS + "new.branch"; - Transport t; PushResult result; - t = Transport.open(db, remoteURI); OutputStream out = new ByteArrayOutputStream(); - try { + try (Transport t = Transport.open(db, remoteURI)) { final String srcExpr = Q.name(); final boolean forceUpdate = false; final String localName = null; @@ -208,8 +202,6 @@ public class HookMessageTest extends HttpTestCase { srcExpr, dstName, forceUpdate, localName, oldId); result = t.push(NullProgressMonitor.INSTANCE, Collections.singleton(update), out); - } finally { - t.close(); } String expectedMessage = "message line 1\n" // diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java index 4c08ec2637..0415bcbb55 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java @@ -144,11 +144,9 @@ public class MeasurePackSizeTest extends HttpTestCase { final RevCommit Q = src.commit().add("Q", Q_txt).create(); final Repository db = src.getRepository(); final String dstName = Constants.R_HEADS + "new.branch"; - Transport t; PushResult result; - t = Transport.open(db, remoteURI); - try { + try (Transport t = Transport.open(db, remoteURI)) { final String srcExpr = Q.name(); final boolean forceUpdate = false; final String localName = null; @@ -158,8 +156,6 @@ public class MeasurePackSizeTest extends HttpTestCase { srcExpr, dstName, forceUpdate, localName, oldId); result = t.push(NullProgressMonitor.INSTANCE, Collections.singleton(update)); - } finally { - t.close(); } assertEquals("expected 1 RemoteUpdate", 1, result.getRemoteUpdates() .size()); diff --git a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs index 64f74989e1..794592dee1 100644 --- a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF index 966af78347..1ff2b6d4ff 100644 --- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.junit.http Bundle-SymbolicName: org.eclipse.jgit.junit.http -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy @@ -22,16 +22,16 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.0)", org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.ssl;version="[9.4.5,10.0.0)", - org.eclipse.jgit.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.http.server;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.junit;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport.resolver;version="[4.10.1,4.11.0)", + org.eclipse.jgit.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.http.server;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.junit;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.resolver;version="[4.11.2,4.12.0)", org.junit;version="[4.12,5.0.0)" -Export-Package: org.eclipse.jgit.junit.http;version="4.10.1"; +Export-Package: org.eclipse.jgit.junit.http;version="4.11.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 6fe484da5d..3796458812 100644 --- a/org.eclipse.jgit.junit.http/pom.xml +++ b/org.eclipse.jgit.junit.http/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.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/TestRequestLog.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java index 43181f2e13..c1abe37d96 100644 --- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java +++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java @@ -59,79 +59,80 @@ import org.eclipse.jetty.server.handler.HandlerWrapper; /** Logs request made through {@link AppServer}. */ class TestRequestLog extends HandlerWrapper { - private static final int MAX = 16; - - private final List<AccessEvent> events = new ArrayList<>(); - - private final Semaphore active = new Semaphore(MAX); - - /** Reset the log back to its original empty state. */ - void clear() { - try { - for (;;) { - try { - active.acquire(MAX); - break; - } catch (InterruptedException e) { - continue; - } - } - - synchronized (events) { - events.clear(); - } - } finally { - active.release(MAX); - } - } - - /** @return all of the events made since the last clear. */ - List<AccessEvent> getEvents() { - try { - for (;;) { - try { - active.acquire(MAX); - break; - } catch (InterruptedException e) { - continue; - } - } - - synchronized (events) { - return events; - } - } finally { - active.release(MAX); - } - } - - /** {@inheritDoc} */ - @Override - public void handle(String target, Request baseRequest, HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { - try { - for (;;) { - try { - active.acquire(); - break; - } catch (InterruptedException e) { - continue; - } - } - - super.handle(target, baseRequest, request, response); - - if (DispatcherType.REQUEST.equals(baseRequest.getDispatcherType())) - log((Request) request, (Response) response); - - } finally { - active.release(); - } - } - - private void log(Request request, Response response) { - synchronized (events) { - events.add(new AccessEvent(request, response)); - } - } + private static final int MAX = 16; + + private final List<AccessEvent> events = new ArrayList<>(); + + private final Semaphore active = new Semaphore(MAX, true); + + /** Reset the log back to its original empty state. */ + void clear() { + try { + for (;;) { + try { + active.acquire(MAX); + break; + } catch (InterruptedException e) { + continue; + } + } + + synchronized (events) { + events.clear(); + } + } finally { + active.release(MAX); + } + } + + /** @return all of the events made since the last clear. */ + List<AccessEvent> getEvents() { + try { + for (;;) { + try { + active.acquire(MAX); + break; + } catch (InterruptedException e) { + continue; + } + } + + synchronized (events) { + return events; + } + } finally { + active.release(MAX); + } + } + + /** {@inheritDoc} */ + @Override + public void handle(String target, Request baseRequest, + HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + try { + for (;;) { + try { + active.acquire(); + break; + } catch (InterruptedException e) { + continue; + } + } + + super.handle(target, baseRequest, request, response); + + if (DispatcherType.REQUEST.equals(baseRequest.getDispatcherType())) + log((Request) request, (Response) response); + + } finally { + active.release(); + } + } + + private void log(Request request, Response response) { + synchronized (events) { + events.add(new AccessEvent(request, response)); + } + } } diff --git a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs index 64f74989e1..794592dee1 100644 --- a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF index 1e60a904f3..9c321db3f0 100644 --- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF @@ -3,31 +3,31 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.junit Bundle-SymbolicName: org.eclipse.jgit.junit -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: org.eclipse.jgit.api;version="[4.10.1,4.11.0)", - org.eclipse.jgit.api.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.dircache;version="[4.10.1,4.11.0)", - org.eclipse.jgit.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.pack;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.merge;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.treewalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.treewalk.filter;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util.io;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util.time;version="[4.10.1,4.11.0)", +Import-Package: org.eclipse.jgit.api;version="[4.11.2,4.12.0)", + org.eclipse.jgit.api.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.dircache;version="[4.11.2,4.12.0)", + org.eclipse.jgit.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.pack;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.merge;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk.filter;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util.io;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util.time;version="[4.11.2,4.12.0)", org.junit;version="[4.12,5.0.0)", org.junit.rules;version="[4.12,5.0.0)", org.junit.runner;version="[4.12,5.0.0)", org.junit.runners.model;version="[4.12,5.0.0)" -Export-Package: org.eclipse.jgit.junit;version="4.10.1"; +Export-Package: org.eclipse.jgit.junit;version="4.11.2"; uses:="org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, @@ -36,4 +36,4 @@ Export-Package: org.eclipse.jgit.junit;version="4.10.1"; org.eclipse.jgit.util, org.eclipse.jgit.storage.file, org.eclipse.jgit.api", - org.eclipse.jgit.junit.time;version="4.10.1" + org.eclipse.jgit.junit.time;version="4.11.2" diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml index df94de6894..74388c7f1d 100644 --- a/org.eclipse.jgit.junit/pom.xml +++ b/org.eclipse.jgit.junit/pom.xml @@ -52,7 +52,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.junit</artifactId> diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java index 4ba2606fb3..cef81a062d 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java @@ -45,6 +45,8 @@ package org.eclipse.jgit.junit; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -180,18 +182,11 @@ public abstract class JGitTestUtil { URL url = cl().getResource(CLASSPATH_TO_RESOURCES + name); if (url == null) throw new FileNotFoundException(name); - InputStream in = url.openStream(); - try { - FileOutputStream out = new FileOutputStream(dest); - try { - byte[] buf = new byte[4096]; - for (int n; (n = in.read(buf)) > 0;) - out.write(buf, 0, n); - } finally { - out.close(); - } - } finally { - in.close(); + try (InputStream in = url.openStream(); + FileOutputStream out = new FileOutputStream(dest)) { + byte[] buf = new byte[4096]; + for (int n; (n = in.read(buf)) > 0;) + out.write(buf, 0, n); } } @@ -250,11 +245,9 @@ public abstract class JGitTestUtil { public static void write(final File f, final String body) throws IOException { FileUtils.mkdirs(f.getParentFile(), true); - Writer w = new OutputStreamWriter(new FileOutputStream(f), "UTF-8"); - try { + try (Writer w = new OutputStreamWriter(new FileOutputStream(f), + UTF_8)) { w.write(body); - } finally { - w.close(); } } @@ -270,7 +263,7 @@ public abstract class JGitTestUtil { */ public static String read(final File file) throws IOException { final byte[] body = IO.readFully(file); - return new String(body, 0, body.length, "UTF-8"); + return new String(body, 0, body.length, UTF_8); } /** diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java index 9dac11e5bc..568bc3b0e1 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java @@ -45,6 +45,7 @@ package org.eclipse.jgit.junit; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; @@ -351,7 +352,7 @@ public abstract class LocalDiskRepositoryTestCase { if (0 != (includedOptions & CONTENT)) { sb.append(", content:" + new String(repo.open(entry.getObjectId(), - Constants.OBJ_BLOB).getCachedBytes(), "UTF-8")); + Constants.OBJ_BLOB).getCachedBytes(), UTF_8)); } if (0 != (includedOptions & ASSUME_UNCHANGED)) sb.append(", assume-unchanged:" diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java index 044f08072a..afc2c445cf 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java @@ -46,6 +46,7 @@ package org.eclipse.jgit.junit; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import java.io.File; @@ -197,14 +198,14 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase { */ protected static void checkFile(File f, final String checkData) throws IOException { - Reader r = new InputStreamReader(new FileInputStream(f), "UTF-8"); - try { - char[] data = new char[checkData.length()]; - if (checkData.length() != r.read(data)) - throw new IOException("Internal error reading file data from "+f); - assertEquals(checkData, new String(data)); - } finally { - r.close(); + try (Reader r = new InputStreamReader(new FileInputStream(f), + UTF_8)) { + if (checkData.length() > 0) { + char[] data = new char[checkData.length()]; + assertEquals(data.length, r.read(data)); + assertEquals(checkData, new String(data)); + } + assertEquals(-1, r.read()); } } diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs index 64f74989e1..794592dee1 100644 --- a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF index 266a084075..73796ef507 100644 --- a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.lfs.server.test Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.8 @@ -28,11 +28,24 @@ Import-Package: javax.servlet;version="[3.1.0,4.0.0)", org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)", - org.eclipse.jgit.junit.http;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs.server.fs;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs.test;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)", + org.eclipse.jgit.api;version="[4.11.2,4.12.0)", + org.eclipse.jgit.api.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.junit;version="[4.11.2,4.12.0)", + org.eclipse.jgit.junit.http;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.server;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.server.fs;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.test;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk.filter;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)", org.hamcrest.core;version="[1.1.0,2.0.0)", org.junit;version="[4.12,5.0.0)", org.junit.rules;version="[4.12,5.0.0)", diff --git a/org.eclipse.jgit.lfs.server.test/pom.xml b/org.eclipse.jgit.lfs.server.test/pom.xml index 7aa399f5c0..eb4142478d 100644 --- a/org.eclipse.jgit.lfs.server.test/pom.xml +++ b/org.eclipse.jgit.lfs.server.test/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs.server.test</artifactId> diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/CheckoutTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/CheckoutTest.java new file mode 100644 index 0000000000..fb6225b1c1 --- /dev/null +++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/CheckoutTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2016, Christian Halstrick <christian.halstrick@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.server.fs; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.junit.JGitTestUtil; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lfs.BuiltinLFS; +import org.eclipse.jgit.lfs.lib.LongObjectId; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.eclipse.jgit.util.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class CheckoutTest extends LfsServerTest { + + Git git; + private TestRepository tdb; + + @Override + @Before + public void setup() throws Exception { + super.setup(); + + BuiltinLFS.register(); + + Path tmp = Files.createTempDirectory("jgit_test_"); + Repository db = FileRepositoryBuilder + .create(tmp.resolve(".git").toFile()); + db.create(); + StoredConfig cfg = db.getConfig(); + cfg.setBoolean(ConfigConstants.CONFIG_FILTER_SECTION, + ConfigConstants.CONFIG_SECTION_LFS, + ConfigConstants.CONFIG_KEY_USEJGITBUILTIN, true); + cfg.setBoolean(ConfigConstants.CONFIG_FILTER_SECTION, + ConfigConstants.CONFIG_SECTION_LFS, + ConfigConstants.CONFIG_KEY_REQUIRED, false); + cfg.setString(ConfigConstants.CONFIG_SECTION_LFS, null, "url", + server.getURI().toString() + "/lfs"); + cfg.save(); + + tdb = new TestRepository<>(db); + tdb.branch("test").commit() + .add(".gitattributes", + "*.bin filter=lfs diff=lfs merge=lfs -text ") + .add("a.bin", + "version https://git-lfs.github.com/spec/v1\noid sha256:8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414\nsize 7\n") + .create(); + git = Git.wrap(db); + tdb.branch("test2").commit().add(".gitattributes", + "*.bin filter=lfs diff=lfs merge=lfs -text ").create(); + } + + @After + public void cleanup() throws Exception { + tdb.getRepository().close(); + FileUtils.delete(tdb.getRepository().getWorkTree(), + FileUtils.RECURSIVE); + } + + @Test + public void testUnknownContent() throws Exception { + git.checkout().setName("test").call(); + // unknown content. We will see the pointer file + assertEquals( + "version https://git-lfs.github.com/spec/v1\noid sha256:8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414\nsize 7\n", + JGitTestUtil.read(git.getRepository(), "a.bin")); + assertEquals("[POST /lfs/objects/batch 200]", + server.getRequests().toString()); + } + + @Test(expected = JGitInternalException.class) + public void testUnknownContentRequired() throws Exception { + StoredConfig cfg = tdb.getRepository().getConfig(); + cfg.setBoolean(ConfigConstants.CONFIG_FILTER_SECTION, + ConfigConstants.CONFIG_SECTION_LFS, + ConfigConstants.CONFIG_KEY_REQUIRED, true); + cfg.save(); + + // must throw + git.checkout().setName("test").call(); + } + + @Test + public void testKnownContent() throws Exception { + putContent( + LongObjectId.fromString( + "8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414"), + "1234567"); + git.checkout().setName("test").call(); + // known content. we will see the actual content of the LFS blob. + assertEquals( + "1234567", + JGitTestUtil.read(git.getRepository(), "a.bin")); + assertEquals( + "[PUT /lfs/objects/8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414 200" + + ", POST /lfs/objects/batch 200" + + ", GET /lfs/objects/8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414 200]", + server.getRequests().toString()); + + git.checkout().setName("test2").call(); + assertFalse(JGitTestUtil.check(git.getRepository(), "a.bin")); + git.checkout().setName("test").call(); + // unknown content. We will see the pointer file + assertEquals("1234567", + JGitTestUtil.read(git.getRepository(), "a.bin")); + assertEquals(3, server.getRequests().size()); + } + +} diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java index 5da502e96e..90fe116804 100644 --- a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java +++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java @@ -75,9 +75,12 @@ import org.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.errors.LfsException; import org.eclipse.jgit.lfs.lib.AnyLongObjectId; import org.eclipse.jgit.lfs.lib.Constants; import org.eclipse.jgit.lfs.lib.LongObjectId; +import org.eclipse.jgit.lfs.server.LargeFileRepository; +import org.eclipse.jgit.lfs.server.LfsProtocolServlet; import org.eclipse.jgit.lfs.test.LongObjectIdTestUtils; import org.eclipse.jgit.util.FileUtils; import org.eclipse.jgit.util.IO; @@ -122,7 +125,27 @@ public abstract class LfsServerTest { this.repository = new FileLfsRepository(null, dir); servlet = new FileLfsServlet(repository, timeout); app.addServlet(new ServletHolder(servlet), "/objects/*"); + + LfsProtocolServlet protocol = new LfsProtocolServlet() { + private static final long serialVersionUID = 1L; + + @Override + protected LargeFileRepository getLargeFileRepository( + LfsRequest request, String path) { + return repository; + } + + @Override + protected LargeFileRepository getLargeFileRepository( + LfsRequest request, String path, String auth) + throws LfsException { + return repository; + } + }; + app.addServlet(new ServletHolder(protocol), "/objects/batch"); + server.setUp(); + this.repository.setUrl(server.getURI() + "/lfs/objects/"); } @After diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java new file mode 100644 index 0000000000..b081a8ef73 --- /dev/null +++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2018, Markus Duft <markus.duft@ssi-schaefer.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.server.fs; + +import static org.junit.Assert.assertEquals; + +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.RemoteAddCommand; +import org.eclipse.jgit.junit.JGitTestUtil; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lfs.BuiltinLFS; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathFilter; +import org.eclipse.jgit.util.FileUtils; +import org.eclipse.jgit.util.IO; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class PushTest extends LfsServerTest { + + Git git; + + private TestRepository localDb; + + private Repository remoteDb; + + @Override + @Before + public void setup() throws Exception { + super.setup(); + + BuiltinLFS.register(); + + Path rtmp = Files.createTempDirectory("jgit_test_"); + remoteDb = FileRepositoryBuilder.create(rtmp.toFile()); + remoteDb.create(true); + + Path tmp = Files.createTempDirectory("jgit_test_"); + Repository db = FileRepositoryBuilder + .create(tmp.resolve(".git").toFile()); + db.create(false); + StoredConfig cfg = db.getConfig(); + cfg.setString("filter", "lfs", "usejgitbuiltin", "true"); + cfg.setString("lfs", null, "url", server.getURI().toString() + "/lfs"); + cfg.save(); + + localDb = new TestRepository<>(db); + localDb.branch("master").commit().add(".gitattributes", + "*.bin filter=lfs diff=lfs merge=lfs -text ").create(); + git = Git.wrap(db); + + URIish uri = new URIish( + "file://" + remoteDb.getDirectory()); + RemoteAddCommand radd = git.remoteAdd(); + radd.setUri(uri); + radd.setName(Constants.DEFAULT_REMOTE_NAME); + radd.call(); + + git.checkout().setName("master").call(); + git.push().call(); + } + + @After + public void cleanup() throws Exception { + remoteDb.close(); + localDb.getRepository().close(); + FileUtils.delete(localDb.getRepository().getWorkTree(), + FileUtils.RECURSIVE); + FileUtils.delete(remoteDb.getDirectory(), FileUtils.RECURSIVE); + } + + @Test + public void testPushSimple() throws Exception { + JGitTestUtil.writeTrashFile(localDb.getRepository(), "a.bin", + "1234567"); + git.add().addFilepattern("a.bin").call(); + RevCommit commit = git.commit().setMessage("add lfs blob").call(); + git.push().call(); + + // check object in remote db, should be LFS pointer + ObjectId id = commit.getId(); + try (RevWalk walk = new RevWalk(remoteDb)) { + RevCommit rc = walk.parseCommit(id); + try (TreeWalk tw = new TreeWalk(walk.getObjectReader())) { + tw.addTree(rc.getTree()); + tw.setFilter(PathFilter.create("a.bin")); + tw.next(); + + assertEquals(tw.getPathString(), "a.bin"); + ObjectLoader ldr = walk.getObjectReader() + .open(tw.getObjectId(0), Constants.OBJ_BLOB); + try(InputStream is = ldr.openStream()) { + assertEquals( + "version https://git-lfs.github.com/spec/v1\noid sha256:8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414\nsize 7\n", + new String(IO + .readWholeStream(is, + (int) ldr.getSize()) + .array())); + } + } + + } + + assertEquals( + "[POST /lfs/objects/batch 200, PUT /lfs/objects/8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414 200]", + server.getRequests().toString()); + } + +} diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs index ede0f7d55d..89394eca41 100644 --- a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF index ddd35a4c9e..1be2ec3075 100644 --- a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF @@ -3,36 +3,36 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.lfs.server Bundle-SymbolicName: org.eclipse.jgit.lfs.server -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name -Export-Package: org.eclipse.jgit.lfs.server;version="4.10.1"; +Export-Package: org.eclipse.jgit.lfs.server;version="4.11.2"; uses:="javax.servlet.http, org.eclipse.jgit.lfs.lib", - org.eclipse.jgit.lfs.server.fs;version="4.10.1"; + org.eclipse.jgit.lfs.server.fs;version="4.11.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.10.1";x-internal:=true, - org.eclipse.jgit.lfs.server.s3;version="4.10.1"; + org.eclipse.jgit.lfs.server.internal;version="4.11.2";x-internal:=true, + org.eclipse.jgit.lfs.server.s3;version="4.11.2"; uses:="org.eclipse.jgit.lfs.server, org.eclipse.jgit.lfs.lib" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: com.google.gson;version="[2.2.4,3.0.0)", +Import-Package: com.google.gson;version="[2.8.0,3.0.0)", javax.servlet;version="[3.1.0,4.0.0)", javax.servlet.annotation;version="[3.1.0,4.0.0)", javax.servlet.http;version="[3.1.0,4.0.0)", org.apache.http;version="[4.3.0,5.0.0)", org.apache.http.client;version="[4.3.0,5.0.0)", - org.eclipse.jgit.annotations;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs.internal;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.nls;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport.http;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport.http.apache;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)", + org.eclipse.jgit.annotations;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.internal;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.nls;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.http;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.http.apache;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)", org.slf4j;version="[1.7.0,2.0.0)" diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml index 351802d782..af54feb777 100644 --- a/org.eclipse.jgit.lfs.server/pom.xml +++ b/org.eclipse.jgit.lfs.server/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs.server</artifactId> diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java index 5b12be6651..688aef2af7 100644 --- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java @@ -67,7 +67,7 @@ import org.eclipse.jgit.lfs.server.Response.Action; */ public class FileLfsRepository implements LargeFileRepository { - private final String url; + private String url; private final Path dir; /** @@ -179,4 +179,21 @@ public class FileLfsRepository implements LargeFileRepository { while (o >= p) dst[o--] = '0'; } + + /** + * @return the url of the content server + * @since 4.11 + */ + public String getUrl() { + return url; + } + + /** + * @param url + * the url of the content server + * @since 4.11 + */ + public void setUrl(String url) { + this.url = url; + } } diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsServlet.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsServlet.java index 018102f793..36dc7606b4 100644 --- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsServlet.java +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsServlet.java @@ -178,10 +178,10 @@ public class FileLfsServlet extends HttpServlet { protected static void sendError(HttpServletResponse rsp, int status, String message) throws IOException { rsp.setStatus(status); - PrintWriter writer = rsp.getWriter(); - LfsGson.toJson(message, writer); - writer.flush(); - writer.close(); + try (PrintWriter writer = rsp.getWriter()) { + LfsGson.toJson(message, writer); + writer.flush(); + } rsp.flushBuffer(); } } diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs index 64f74989e1..794592dee1 100644 --- a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF index 5ae0989ffa..54c884d8b5 100644 --- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF @@ -3,23 +3,23 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.lfs.test Bundle-SymbolicName: org.eclipse.jgit.lfs.test -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[4.10.1,4.11.0)", - org.eclipse.jgit.junit;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.treewalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.treewalk.filter;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)", +Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[4.11.2,4.12.0)", + org.eclipse.jgit.junit;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk.filter;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)", org.hamcrest.core;version="[1.1.0,2.0.0)", org.junit;version="[4.12,5.0.0)", org.junit.runner;version="[4.12,5.0.0)", org.junit.runners;version="[4.12,5.0.0)" -Export-Package: org.eclipse.jgit.lfs.test;version="4.10.1";x-friends:="org.eclipse.jgit.lfs.server.test" +Export-Package: org.eclipse.jgit.lfs.test;version="4.11.2";x-friends:="org.eclipse.jgit.lfs.server.test" diff --git a/org.eclipse.jgit.lfs.test/pom.xml b/org.eclipse.jgit.lfs.test/pom.xml index 6206ba057d..39c238603e 100644 --- a/org.eclipse.jgit.lfs.test/pom.xml +++ b/org.eclipse.jgit.lfs.test/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs.test</artifactId> diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java index 2ace2e3ce6..146a25ed3d 100644 --- a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java +++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java @@ -61,11 +61,12 @@ public class LFSPointerTest { final String s = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10"; AnyLongObjectId id = LongObjectId.fromString(s); LfsPointer ptr = new LfsPointer(id, 4); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ptr.encode(baos); - baos.close(); - assertEquals("version https://git-lfs.github.com/spec/v1\noid sha256:" - + s + "\nsize 4\n", - baos.toString(UTF_8.name())); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + ptr.encode(baos); + assertEquals( + "version https://git-lfs.github.com/spec/v1\noid sha256:" + + s + "\nsize 4\n", + baos.toString(UTF_8.name())); + } } } diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java index d6dd3aa607..8642e7eb3b 100644 --- a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java +++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java @@ -392,9 +392,10 @@ public class LongObjectIdTest { public void testCopyToWriter() throws IOException { AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test"); ByteArrayOutputStream os = new ByteArrayOutputStream(64); - OutputStreamWriter w = new OutputStreamWriter(os, Constants.CHARSET); - id1.copyTo(w); - w.close(); + try (OutputStreamWriter w = new OutputStreamWriter(os, + Constants.CHARSET)) { + id1.copyTo(w); + } assertEquals(id1, LongObjectId.fromString(os.toByteArray(), 0)); } @@ -402,10 +403,11 @@ public class LongObjectIdTest { public void testCopyToWriterWithBuf() throws IOException { AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test"); ByteArrayOutputStream os = new ByteArrayOutputStream(64); - OutputStreamWriter w = new OutputStreamWriter(os, Constants.CHARSET); - char[] buf = new char[64]; - id1.copyTo(buf, w); - w.close(); + try (OutputStreamWriter w = new OutputStreamWriter(os, + Constants.CHARSET)) { + char[] buf = new char[64]; + id1.copyTo(buf, w); + } assertEquals(id1, LongObjectId.fromString(os.toByteArray(), 0)); } diff --git a/org.eclipse.jgit.lfs/.settings/.api_filters b/org.eclipse.jgit.lfs/.settings/.api_filters new file mode 100644 index 0000000000..f4887e272b --- /dev/null +++ b/org.eclipse.jgit.lfs/.settings/.api_filters @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<component id="org.eclipse.jgit.lfs" version="2"> + <resource path="src/org/eclipse/jgit/lfs/CleanFilter.java" type="org.eclipse.jgit.lfs.CleanFilter"> + <filter id="421572723"> + <message_arguments> + <message_argument value="org.eclipse.jgit.lfs.CleanFilter"/> + <message_argument value="register()"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/lfs/LfsPointer.java" type="org.eclipse.jgit.lfs.LfsPointer"> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.lfs.LfsPointer"/> + <message_argument value="SIZE_THRESHOLD"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/lfs/SmudgeFilter.java" type="org.eclipse.jgit.lfs.SmudgeFilter"> + <filter id="421572723"> + <message_arguments> + <message_argument value="org.eclipse.jgit.lfs.SmudgeFilter"/> + <message_argument value="register()"/> + </message_arguments> + </filter> + </resource> +</component> diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs index ede0f7d55d..89394eca41 100644 --- a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.lfs/BUILD b/org.eclipse.jgit.lfs/BUILD index 0c7b1b2c4c..cd291dadb9 100644 --- a/org.eclipse.jgit.lfs/BUILD +++ b/org.eclipse.jgit.lfs/BUILD @@ -6,6 +6,7 @@ java_library( resource_strip_prefix = "org.eclipse.jgit.lfs/resources", resources = glob(["resources/**"]), deps = [ + "//lib:gson", "//org.eclipse.jgit:jgit", ], ) diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF index 74215879ee..322839fc2c 100644 --- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF @@ -3,20 +3,33 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.lfs Bundle-SymbolicName: org.eclipse.jgit.lfs -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name -Export-Package: org.eclipse.jgit.lfs;version="4.10.1", - org.eclipse.jgit.lfs.errors;version="4.10.1", - org.eclipse.jgit.lfs.internal;version="4.10.1";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server", - org.eclipse.jgit.lfs.lib;version="4.10.1" +Export-Package: org.eclipse.jgit.lfs;version="4.11.2", + org.eclipse.jgit.lfs.errors;version="4.11.2", + org.eclipse.jgit.lfs.internal;version="4.11.2";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server", + org.eclipse.jgit.lfs.lib;version="4.11.2" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: org.eclipse.jgit.annotations;version="[4.10.1,4.11.0)";resolution:=optional, - org.eclipse.jgit.attributes;version="[4.10.1,4.11.0)", - org.eclipse.jgit.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.nls;version="[4.10.1,4.11.0)", - org.eclipse.jgit.treewalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.treewalk.filter;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)" +Import-Package: com.google.gson;version="[2.8.2,3.0.0)", + com.google.gson.stream;version="[2.8.2,3.0.0)", + org.apache.http.impl.client;version="[4.2.6,5.0.0)", + org.apache.http.impl.conn;version="[4.2.6,5.0.0)", + org.eclipse.jgit.annotations;version="[4.11.2,4.12.0)";resolution:=optional, + org.eclipse.jgit.api.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.attributes;version="[4.11.2,4.12.0)", + org.eclipse.jgit.diff;version="[4.11.2,4.12.0)", + org.eclipse.jgit.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.hooks;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.nls;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.storage.pack;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.http;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk.filter;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util.io;version="[4.11.2,4.12.0)" diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml index 8585ccfa5f..74649bb9cc 100644 --- a/org.eclipse.jgit.lfs/pom.xml +++ b/org.eclipse.jgit.lfs/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs</artifactId> @@ -70,6 +70,10 @@ <artifactId>org.eclipse.jgit</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + </dependency> </dependencies> <build> <sourceDirectory>src/</sourceDirectory> diff --git a/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties b/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties index e08e28cc55..0e00f146ae 100644 --- a/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties +++ b/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties @@ -1,11 +1,19 @@ corruptLongObject=The content hash ''{0}'' of the long object ''{1}'' doesn''t match its id, the corrupt object will be deleted. incorrectLONG_OBJECT_ID_LENGTH=Incorrect LONG_OBJECT_ID_LENGTH. -inconsistentMediafileLength=mediafile {0} has unexpected length; expected {1} but found {2}. +inconsistentMediafileLength=Mediafile {0} has unexpected length; expected {1} but found {2}. +inconsistentContentLength=Unexpected content length reported by LFS server ({0}), expected {1} but reported was {2} invalidLongId=Invalid id: {0} invalidLongIdLength=Invalid id length {0}; should be {1} +lfsUnavailable=LFS is not available for repository {0} +protocolError=LFS Protocol Error {0}: {1} requiredHashFunctionNotAvailable=Required hash function {0} not available. repositoryNotFound=Repository {0} not found repositoryReadOnly=Repository {0} is read-only lfsUnavailable=LFS is not available for repository {0} lfsUnathorized=Not authorized to perform operation {0} on repository {1} lfsFailedToGetRepository=failed to get repository {0} +lfsNoDownloadUrl="Need to download object from LFS server but couldn't determine LFS server URL" +serverFailure=When trying to open a connection to {0} the server responded with an error code. rc={1} +wrongAmoutOfDataReceived=While downloading data from the content server {0} {1} bytes have been received while {2} have been expected +userConfigInvalid="User config file {0} invalid {1}" +missingLocalObject="Local Object {0} is missing"
\ No newline at end of file diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java new file mode 100644 index 0000000000..415caa9859 --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2017, Markus Duft <markus.duft@ssi-schaefer.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; + +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.attributes.Attribute; +import org.eclipse.jgit.hooks.PrePushHook; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.util.LfsFactory; + +/** + * Implementation of {@link LfsFactory}, using built-in (optional) LFS support. + * + * @since 4.11 + */ +public class BuiltinLFS extends LfsFactory { + + private BuiltinLFS() { + SmudgeFilter.register(); + CleanFilter.register(); + } + + /** + * Activates the built-in LFS support. + */ + public static void register() { + setInstance(new BuiltinLFS()); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public ObjectLoader applySmudgeFilter(Repository db, ObjectLoader loader, + Attribute attribute) throws IOException { + if (isEnabled(db) && (attribute == null || isEnabled(db, attribute))) { + return LfsBlobFilter.smudgeLfsBlob(db, loader); + } else { + return loader; + } + } + + @Override + public LfsInputStream applyCleanFilter(Repository db, InputStream input, + long length, Attribute attribute) throws IOException { + if (isEnabled(db, attribute)) { + return new LfsInputStream(LfsBlobFilter.cleanLfsBlob(db, input)); + } else { + return new LfsInputStream(input, length); + } + } + + @Override + public @Nullable PrePushHook getPrePushHook(Repository repo, + PrintStream outputStream) { + if (isEnabled(repo)) { + return new LfsPrePushHook(repo, outputStream); + } + return null; + } + + /** + * @param db + * the repository + * @return whether LFS is requested for the given repo. + */ + @Override + public boolean isEnabled(Repository db) { + if (db == null) { + return false; + } + return db.getConfig().getBoolean(ConfigConstants.CONFIG_FILTER_SECTION, + ConfigConstants.CONFIG_SECTION_LFS, + ConfigConstants.CONFIG_KEY_USEJGITBUILTIN, + false); + } + + /** + * @param db + * the repository + * @param attribute + * the attribute to check + * @return whether LFS filter is enabled for the given .gitattribute + * attribute. + */ + private boolean isEnabled(Repository db, Attribute attribute) { + if (attribute == null) { + return false; + } + return isEnabled(db) && ConfigConstants.CONFIG_SECTION_LFS + .equals(attribute.getValue()); + } + + @Override + public LfsInstallCommand getInstallCommand() { + return new InstallBuiltinLfsCommand(); + } + +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java index 4a98286ddb..217e46f48e 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java @@ -55,6 +55,7 @@ import org.eclipse.jgit.attributes.FilterCommandRegistry; import org.eclipse.jgit.lfs.errors.CorruptMediaFile; import org.eclipse.jgit.lfs.internal.AtomicObjectOutputStream; import org.eclipse.jgit.lfs.lib.AnyLongObjectId; +import org.eclipse.jgit.lfs.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.util.FileUtils; @@ -90,11 +91,12 @@ public class CleanFilter extends FilterCommand { * Registers this filter by calling * {@link FilterCommandRegistry#register(String, FilterCommandFactory)} */ - public final static void register() { - FilterCommandRegistry.register( - org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX - + "lfs/clean", //$NON-NLS-1$ - FACTORY); + static void register() { + FilterCommandRegistry + .register(org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX + + Constants.ATTR_FILTER_DRIVER_PREFIX + + org.eclipse.jgit.lib.Constants.ATTR_FILTER_TYPE_CLEAN, + FACTORY); } // Used to compute the hash for the original content @@ -127,7 +129,7 @@ public class CleanFilter extends FilterCommand { public CleanFilter(Repository db, InputStream in, OutputStream out) throws IOException { super(in, out); - lfsUtil = new Lfs(FileUtils.toPath(db.getDirectory()).resolve("lfs")); //$NON-NLS-1$ + lfsUtil = new Lfs(db); Files.createDirectories(lfsUtil.getLfsTmpDir()); tmpFile = lfsUtil.createTmpFile(); this.aOut = new AtomicObjectOutputStream(tmpFile.toAbsolutePath()); @@ -165,6 +167,7 @@ public class CleanFilter extends FilterCommand { } LfsPointer lfsPointer = new LfsPointer(loid, size); lfsPointer.encode(out); + in.close(); out.close(); return -1; } @@ -172,6 +175,7 @@ public class CleanFilter extends FilterCommand { if (aOut != null) { aOut.abort(); } + in.close(); out.close(); throw e; } diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/InstallBuiltinLfsCommand.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/InstallBuiltinLfsCommand.java new file mode 100644 index 0000000000..028b19b2ab --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/InstallBuiltinLfsCommand.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2018, Markus Duft <markus.duft@ssi-schaefer.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs; + +import java.io.IOException; +import java.text.MessageFormat; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lfs.internal.LfsText; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.LfsFactory.LfsInstallCommand; +import org.eclipse.jgit.util.SystemReader; + +/** + * Installs all required LFS properties for the current user, analogous to 'git + * lfs install', but defaulting to using JGit builtin hooks. + * + * @since 4.11 + */ +public class InstallBuiltinLfsCommand implements LfsInstallCommand { + + private static final String[] ARGS_USER = new String[] { "lfs", "install" }; //$NON-NLS-1$//$NON-NLS-2$ + + private static final String[] ARGS_LOCAL = new String[] { "lfs", "install", //$NON-NLS-1$//$NON-NLS-2$ + "--local" }; //$NON-NLS-1$ + + private Repository repository; + + /** {@inheritDoc} */ + @Override + public Void call() throws Exception { + StoredConfig cfg = null; + if (repository == null) { + cfg = loadUserConfig(); + } else { + cfg = repository.getConfig(); + } + + cfg.setBoolean(ConfigConstants.CONFIG_FILTER_SECTION, + ConfigConstants.CONFIG_SECTION_LFS, + ConfigConstants.CONFIG_KEY_USEJGITBUILTIN, true); + cfg.setBoolean(ConfigConstants.CONFIG_FILTER_SECTION, + ConfigConstants.CONFIG_SECTION_LFS, + ConfigConstants.CONFIG_KEY_REQUIRED, true); + + cfg.save(); + + // try to run git lfs install, we really don't care if it is present + // and/or works here (yet). + ProcessBuilder builder = FS.DETECTED.runInShell("git", //$NON-NLS-1$ + repository == null ? ARGS_USER : ARGS_LOCAL); + if (repository != null) { + builder.directory(repository.isBare() ? repository.getDirectory() + : repository.getWorkTree()); + } + FS.DETECTED.runProcess(builder, null, null, (String) null); + + return null; + } + + /** + * Set the repository to install LFS for + * + * @param repo + * the repository to install LFS into locally instead of the user + * configuration + * @return this command + */ + @Override + public LfsInstallCommand setRepository(Repository repo) { + this.repository = repo; + return this; + } + + private StoredConfig loadUserConfig() throws IOException { + FileBasedConfig c = SystemReader.getInstance().openUserConfig(null, + FS.DETECTED); + try { + c.load(); + } catch (ConfigInvalidException e1) { + throw new IOException(MessageFormat + .format(LfsText.get().userConfigInvalid, c.getFile() + .getAbsolutePath(), e1), + e1); + } + + return c; + } + +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Lfs.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Lfs.java index 138996d82f..40d83420ec 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Lfs.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Lfs.java @@ -47,6 +47,8 @@ import java.nio.file.Files; import java.nio.file.Path; import org.eclipse.jgit.lfs.lib.AnyLongObjectId; +import org.eclipse.jgit.lfs.lib.Constants; +import org.eclipse.jgit.lib.Repository; /** * Class which represents the lfs folder hierarchy inside a {@code .git} folder @@ -66,12 +68,26 @@ public class Lfs { * @param root * the path to the LFS media directory. Will be * {@code "<repo>/.git/lfs"} + * @deprecated use {@link #Lfs(Repository)} instead. */ + @Deprecated public Lfs(Path root) { this.root = root; } /** + * Constructor for Lfs. + * + * @param db + * the associated repo + * + * @since 4.11 + */ + public Lfs(Repository db) { + this.root = db.getDirectory().toPath().resolve(Constants.LFS); + } + + /** * Get the LFS root directory * * @return the path to the LFS directory @@ -118,7 +134,7 @@ public class Lfs { public Path getMediaFile(AnyLongObjectId id) { String idStr = id.name(); return getLfsObjDir().resolve(idStr.substring(0, 2)) - .resolve(idStr.substring(2)); + .resolve(idStr.substring(2, 4)).resolve(idStr); } /** diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsBlobFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsBlobFilter.java new file mode 100644 index 0000000000..a7d218f803 --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsBlobFilter.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2017, Markus Duft <markus.duft@ssi-schaefer.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jgit.lfs.lib.AnyLongObjectId; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.util.TemporaryBuffer; +import org.eclipse.jgit.util.TemporaryBuffer.LocalFile; + +/** + * Provides transparently either a stream to the blob or a LFS media file if + * managed by LFS. + * + * @since 4.11 + */ +public class LfsBlobFilter { + + /** + * In case the given {@link ObjectLoader} points to a LFS pointer file + * replace the loader with one pointing to the LFS media file contents. + * Missing LFS files are downloaded on the fly - same logic as the smudge + * filter. + * + * @param db + * the repo + * @param loader + * the loader for the blob + * @return either the original loader, or a loader for the LFS media file if + * managed by LFS. Files are downloaded on demand if required. + * @throws IOException + * in case of an error + */ + public static ObjectLoader smudgeLfsBlob(Repository db, ObjectLoader loader) + throws IOException { + if (loader.getSize() > LfsPointer.SIZE_THRESHOLD) { + return loader; + } + + try (InputStream is = loader.openStream()) { + LfsPointer ptr = LfsPointer.parseLfsPointer(is); + if (ptr != null) { + Lfs lfs = new Lfs(db); + AnyLongObjectId oid = ptr.getOid(); + Path mediaFile = lfs.getMediaFile(oid); + if (!Files.exists(mediaFile)) { + SmudgeFilter.downloadLfsResource(lfs, db, ptr); + } + + return new LfsBlobLoader(mediaFile); + } + } + + return loader; + } + + /** + * Run the LFS clean filter on the given stream and return a stream to the + * LFS pointer file buffer. Used when inserting objects. + * + * @param db + * the {@link Repository} + * @param originalContent + * the {@link InputStream} to the original content + * @return a {@link TemporaryBuffer} representing the LFS pointer. The + * caller is responsible to destroy the buffer. + * @throws IOException + * in case of any error. + */ + public static TemporaryBuffer cleanLfsBlob(Repository db, + InputStream originalContent) throws IOException { + LocalFile buffer = new TemporaryBuffer.LocalFile(null); + CleanFilter f = new CleanFilter(db, originalContent, buffer); + try { + while (f.run() != -1) { + // loop as long as f.run() tells there is work to do + } + } catch (IOException e) { + buffer.destroy(); + throw e; + } + return buffer; + } + +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsBlobLoader.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsBlobLoader.java new file mode 100644 index 0000000000..697ae47a68 --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsBlobLoader.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2017, Markus Duft <markus.duft@ssi-schaefer.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; + +import org.eclipse.jgit.errors.LargeObjectException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.lib.ObjectStream; +import org.eclipse.jgit.storage.pack.PackConfig; +import org.eclipse.jgit.util.IO; + +/** + * An {@link ObjectLoader} implementation that reads a media file from the LFS + * storage. + * + * @since 4.11 + */ +public class LfsBlobLoader extends ObjectLoader { + + private Path mediaFile; + + private BasicFileAttributes attributes; + + private byte[] cached; + + /** + * Create a loader for the LFS media file at the given path. + * + * @param mediaFile + * path to the file + * @throws IOException + * in case of an error reading attributes + */ + public LfsBlobLoader(Path mediaFile) throws IOException { + this.mediaFile = mediaFile; + this.attributes = Files.readAttributes(mediaFile, + BasicFileAttributes.class); + } + + @Override + public int getType() { + return Constants.OBJ_BLOB; + } + + @Override + public long getSize() { + return attributes.size(); + } + + @Override + public byte[] getCachedBytes() throws LargeObjectException { + if (getSize() > PackConfig.DEFAULT_BIG_FILE_THRESHOLD) { + throw new LargeObjectException(); + } + + if (cached == null) { + try { + cached = IO.readFully(mediaFile.toFile()); + } catch (IOException ioe) { + throw new LargeObjectException(ioe); + } + } + return cached; + } + + @Override + public ObjectStream openStream() + throws MissingObjectException, IOException { + return new ObjectStream.Filter(getType(), getSize(), + Files.newInputStream(mediaFile)); + } + +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java index 360453116f..0e3830c098 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java @@ -64,7 +64,7 @@ import org.eclipse.jgit.lfs.lib.LongObjectId; * * @since 4.6 */ -public class LfsPointer { +public class LfsPointer implements Comparable<LfsPointer> { /** * The version of the LfsPointer file format */ @@ -77,6 +77,13 @@ public class LfsPointer { public static final String VERSION_LEGACY = "https://hawser.github.com/spec/v1"; //$NON-NLS-1$ /** + * Don't inspect files that are larger than this threshold to avoid + * excessive reading. No pointer file should be larger than this. + * @since 4.11 + */ + public static final int SIZE_THRESHOLD = 200; + + /** * The name of the hash function as used in the pointer files. This will * evaluate to "sha256" */ @@ -185,5 +192,18 @@ public class LfsPointer { return "LfsPointer: oid=" + oid.name() + ", size=" //$NON-NLS-1$ //$NON-NLS-2$ + size; } + + /** + * @since 4.11 + */ + @Override + public int compareTo(LfsPointer o) { + int x = getOid().compareTo(o.getOid()); + if (x != 0) { + return x; + } + + return (int) (getSize() - o.getSize()); + } } diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java new file mode 100644 index 0000000000..6115e39813 --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2017, Markus Duft <markus.duft@ssi-schaefer.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.eclipse.jgit.lfs.Protocol.OPERATION_UPLOAD; +import static org.eclipse.jgit.lfs.internal.LfsConnectionFactory.toRequest; +import static org.eclipse.jgit.transport.http.HttpConnection.HTTP_OK; +import static org.eclipse.jgit.util.HttpSupport.METHOD_POST; +import static org.eclipse.jgit.util.HttpSupport.METHOD_PUT; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.jgit.api.errors.AbortedByHookException; +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.hooks.PrePushHook; +import org.eclipse.jgit.lfs.Protocol.ObjectInfo; +import org.eclipse.jgit.lfs.errors.CorruptMediaFile; +import org.eclipse.jgit.lfs.internal.LfsConnectionFactory; +import org.eclipse.jgit.lfs.internal.LfsText; +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefDatabase; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.ObjectWalk; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.transport.RemoteRefUpdate; +import org.eclipse.jgit.transport.http.HttpConnection; + +import com.google.gson.Gson; +import com.google.gson.stream.JsonReader; + +/** + * Pre-push hook that handles uploading LFS artefacts. + * + * @since 4.11 + */ +public class LfsPrePushHook extends PrePushHook { + + private static final String EMPTY = ""; //$NON-NLS-1$ + private Collection<RemoteRefUpdate> refs; + + /** + * @param repo + * the repository + * @param outputStream + * not used by this implementation + */ + public LfsPrePushHook(Repository repo, PrintStream outputStream) { + super(repo, outputStream); + } + + @Override + public void setRefs(Collection<RemoteRefUpdate> toRefs) { + this.refs = toRefs; + } + + @Override + public String call() throws IOException, AbortedByHookException { + Set<LfsPointer> toPush = findObjectsToPush(); + if (toPush.isEmpty()) { + return EMPTY; + } + HttpConnection api = LfsConnectionFactory.getLfsConnection( + getRepository(), METHOD_POST, OPERATION_UPLOAD); + Map<String, LfsPointer> oid2ptr = requestBatchUpload(api, toPush); + uploadContents(api, oid2ptr); + return EMPTY; + + } + + private Set<LfsPointer> findObjectsToPush() throws IOException, + MissingObjectException, IncorrectObjectTypeException { + Set<LfsPointer> toPush = new TreeSet<>(); + + try (ObjectWalk walk = new ObjectWalk(getRepository())) { + for (RemoteRefUpdate up : refs) { + walk.setRewriteParents(false); + excludeRemoteRefs(walk); + walk.markStart(walk.parseCommit(up.getNewObjectId())); + while (walk.next() != null) { + // walk all commits to populate objects + } + findLfsPointers(toPush, walk); + } + } + return toPush; + } + + private static void findLfsPointers(Set<LfsPointer> toPush, ObjectWalk walk) + throws MissingObjectException, IncorrectObjectTypeException, + IOException { + RevObject obj; + ObjectReader r = walk.getObjectReader(); + while ((obj = walk.nextObject()) != null) { + if (obj.getType() == Constants.OBJ_BLOB + && getObjectSize(r, obj) < LfsPointer.SIZE_THRESHOLD) { + LfsPointer ptr = loadLfsPointer(r, obj); + if (ptr != null) { + toPush.add(ptr); + } + } + } + } + + private static long getObjectSize(ObjectReader r, RevObject obj) + throws IOException { + return r.getObjectSize(obj.getId(), Constants.OBJ_BLOB); + } + + private static LfsPointer loadLfsPointer(ObjectReader r, AnyObjectId obj) + throws IOException { + try (InputStream is = r.open(obj, Constants.OBJ_BLOB).openStream()) { + return LfsPointer.parseLfsPointer(is); + } + } + + private void excludeRemoteRefs(ObjectWalk walk) throws IOException { + RefDatabase refDatabase = getRepository().getRefDatabase(); + Map<String, Ref> remoteRefs = refDatabase.getRefs(remote()); + for (Ref r : remoteRefs.values()) { + ObjectId oid = r.getPeeledObjectId(); + if (oid == null) { + oid = r.getObjectId(); + } + if (oid == null) { + // ignore (e.g. symbolic, ...) + continue; + } + RevObject o = walk.parseAny(oid); + if (o.getType() == Constants.OBJ_COMMIT + || o.getType() == Constants.OBJ_TAG) { + walk.markUninteresting(o); + } + } + } + + private String remote() { + String remoteName = getRemoteName() == null + ? Constants.DEFAULT_REMOTE_NAME + : getRemoteName(); + return Constants.R_REMOTES + remoteName; + } + + private Map<String, LfsPointer> requestBatchUpload(HttpConnection api, + Set<LfsPointer> toPush) throws IOException { + LfsPointer[] res = toPush.toArray(new LfsPointer[toPush.size()]); + Map<String, LfsPointer> oidStr2ptr = new HashMap<>(); + for (LfsPointer p : res) { + oidStr2ptr.put(p.getOid().name(), p); + } + Gson gson = Protocol.gson(); + api.getOutputStream().write( + gson.toJson(toRequest(OPERATION_UPLOAD, res)).getBytes(UTF_8)); + int responseCode = api.getResponseCode(); + if (responseCode != HTTP_OK) { + throw new IOException( + MessageFormat.format(LfsText.get().serverFailure, + api.getURL(), Integer.valueOf(responseCode))); + } + return oidStr2ptr; + } + + private void uploadContents(HttpConnection api, + Map<String, LfsPointer> oid2ptr) throws IOException { + try (JsonReader reader = new JsonReader( + new InputStreamReader(api.getInputStream()))) { + for (Protocol.ObjectInfo o : parseObjects(reader)) { + if (o.actions == null) { + continue; + } + LfsPointer ptr = oid2ptr.get(o.oid); + if (ptr == null) { + // received an object we didn't request + continue; + } + Protocol.Action uploadAction = o.actions.get(OPERATION_UPLOAD); + if (uploadAction == null || uploadAction.href == null) { + continue; + } + + Lfs lfs = new Lfs(getRepository()); + Path path = lfs.getMediaFile(ptr.getOid()); + if (!Files.exists(path)) { + throw new IOException(MessageFormat + .format(LfsText.get().missingLocalObject, path)); + } + uploadFile(o, uploadAction, path); + } + } + } + + private List<ObjectInfo> parseObjects(JsonReader reader) { + Gson gson = new Gson(); + Protocol.Response resp = gson.fromJson(reader, Protocol.Response.class); + return resp.objects; + } + + private void uploadFile(Protocol.ObjectInfo o, + Protocol.Action uploadAction, Path path) + throws IOException, CorruptMediaFile { + HttpConnection contentServer = LfsConnectionFactory + .getLfsContentConnection(getRepository(), uploadAction, + METHOD_PUT); + contentServer.setDoOutput(true); + try (OutputStream out = contentServer + .getOutputStream()) { + long size = Files.copy(path, out); + if (size != o.size) { + throw new CorruptMediaFile(path, o.size, size); + } + } + int responseCode = contentServer.getResponseCode(); + if (responseCode != HTTP_OK) { + throw new IOException(MessageFormat.format( + LfsText.get().serverFailure, contentServer.getURL(), + Integer.valueOf(responseCode))); + } + } +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Protocol.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Protocol.java new file mode 100644 index 0000000000..d88742ed9e --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Protocol.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2016, Christian Halstrick <christian.halstrick@sap.com> + * Copyright (C) 2015, Sasa Zivkov <sasa.zivkov@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs; + +import java.util.List; +import java.util.Map; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * This interface describes the network protocol used between lfs client and lfs + * server + * + * @since 4.11 + */ +public interface Protocol { + /** A request sent to an LFS server */ + class Request { + /** The operation of this request */ + public String operation; + + /** The objects of this request */ + public List<ObjectSpec> objects; + } + + /** A response received from an LFS server */ + class Response { + public List<ObjectInfo> objects; + } + + /** + * MetaData of an LFS object. Needs to be specified when requesting objects + * from the LFS server and is also returned in the response + */ + class ObjectSpec { + public String oid; // the objectid + + public long size; // the size of the object + } + + /** + * Describes in a response all actions the LFS server offers for a single + * object + */ + class ObjectInfo extends ObjectSpec { + public Map<String, Action> actions; // Maps operation to action + + public Error error; + } + + /** + * Describes in a Response a single action the client can execute on a + * single object + */ + class Action { + public String href; + + public Map<String, String> header; + } + + /** + * An action with an additional expiration timestamp + * + * @since 4.11 + */ + class ExpiringAction extends Action { + /** + * Absolute date/time in format "yyyy-MM-dd'T'HH:mm:ss.SSSX" + */ + public String expiresAt; + + /** + * Validity time in milliseconds (preferred over expiresAt as specified: + * https://github.com/git-lfs/git-lfs/blob/master/docs/api/authentication.md) + */ + public String expiresIn; + } + + /** Describes an error to be returned by the LFS batch API */ + class Error { + public int code; + + public String message; + } + + /** + * The "download" operation + */ + String OPERATION_DOWNLOAD = "download"; //$NON-NLS-1$ + + /** + * The "upload" operation + */ + String OPERATION_UPLOAD = "upload"; //$NON-NLS-1$ + + /** + * The contenttype used in LFS requests + */ + String CONTENTTYPE_VND_GIT_LFS_JSON = "application/vnd.git-lfs+json; charset=utf-8"; //$NON-NLS-1$ + + /** + * Authorization header when auto-discovering via SSH. + */ + String HDR_AUTH = "Authorization"; //$NON-NLS-1$ + + /** + * Prefix of authentication token obtained through SSH. + */ + String HDR_AUTH_SSH_PREFIX = "Ssh: "; //$NON-NLS-1$ + + /** + * Path to the LFS info servlet. + */ + String INFO_LFS_ENDPOINT = "/info/lfs"; //$NON-NLS-1$ + + /** + * Path to the LFS objects servlet. + */ + String OBJECTS_LFS_ENDPOINT = "/objects/batch"; //$NON-NLS-1$ + + /** + * @return a {@link Gson} instance suitable for handling this + * {@link Protocol} + * + * @since 4.11 + */ + public static Gson gson() { + return new GsonBuilder() + .setFieldNamingPolicy( + FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .disableHtmlEscaping().create(); + } +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java index 941edec1d8..e2ab309230 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java @@ -44,16 +44,30 @@ package org.eclipse.jgit.lfs; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import org.eclipse.jgit.attributes.FilterCommand; import org.eclipse.jgit.attributes.FilterCommandFactory; import org.eclipse.jgit.attributes.FilterCommandRegistry; +import org.eclipse.jgit.lfs.internal.LfsConnectionFactory; +import org.eclipse.jgit.lfs.internal.LfsText; +import org.eclipse.jgit.lfs.lib.AnyLongObjectId; import org.eclipse.jgit.lfs.lib.Constants; import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.util.FileUtils; +import org.eclipse.jgit.transport.http.HttpConnection; +import org.eclipse.jgit.util.HttpSupport; + +import com.google.gson.Gson; +import com.google.gson.stream.JsonReader; /** * Built-in LFS smudge filter @@ -62,13 +76,17 @@ import org.eclipse.jgit.util.FileUtils; * and this filter is configured for that content, then this filter will replace * the content of LFS pointer files with the original content. This happens e.g. * when a checkout needs to update a working tree file which is under LFS - * control. This implementation expects that the origin content is already - * available in the .git/lfs/objects folder. This implementation will not - * contact any LFS servers in order to get the missing content. + * control. * * @since 4.6 */ public class SmudgeFilter extends FilterCommand { + + /** + * Max number of bytes to copy in a single {@link #run()} call. + */ + private static final int MAX_COPY_BYTES = 1024 * 1024 * 256; + /** * The factory is responsible for creating instances of * {@link org.eclipse.jgit.lfs.SmudgeFilter} @@ -82,52 +100,180 @@ public class SmudgeFilter extends FilterCommand { }; /** - * Registers this filter in JGit by calling + * Register this filter in JGit */ - public final static void register() { - FilterCommandRegistry.register( - org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX - + "lfs/smudge", //$NON-NLS-1$ - FACTORY); + static void register() { + FilterCommandRegistry + .register(org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX + + Constants.ATTR_FILTER_DRIVER_PREFIX + + org.eclipse.jgit.lib.Constants.ATTR_FILTER_TYPE_SMUDGE, + FACTORY); } - private Lfs lfs; - /** * Constructor for SmudgeFilter. * * @param db * a {@link org.eclipse.jgit.lib.Repository} object. * @param in - * a {@link java.io.InputStream} object. + * a {@link java.io.InputStream} object. The stream is closed in + * any case. * @param out * a {@link java.io.OutputStream} object. * @throws java.io.IOException + * in case of an error */ public SmudgeFilter(Repository db, InputStream in, OutputStream out) throws IOException { super(in, out); - lfs = new Lfs(FileUtils.toPath(db.getDirectory()).resolve(Constants.LFS)); - LfsPointer res = LfsPointer.parseLfsPointer(in); - if (res != null) { - Path mediaFile = lfs.getMediaFile(res.getOid()); - if (Files.exists(mediaFile)) { + try { + Lfs lfs = new Lfs(db); + LfsPointer res = LfsPointer.parseLfsPointer(in); + if (res != null) { + AnyLongObjectId oid = res.getOid(); + Path mediaFile = lfs.getMediaFile(oid); + if (!Files.exists(mediaFile)) { + downloadLfsResource(lfs, db, res); + } this.in = Files.newInputStream(mediaFile); } + } finally { + in.close(); // make sure the swapped stream is closed properly. } } + /** + * Download content which is hosted on a LFS server + * + * @param lfs + * local {@link Lfs} storage. + * @param db + * the repository to work with + * @param res + * the objects to download + * @return the paths of all mediafiles which have been downloaded + * @throws IOException + * @since 4.11 + */ + public static Collection<Path> downloadLfsResource(Lfs lfs, Repository db, + LfsPointer... res) throws IOException { + Collection<Path> downloadedPaths = new ArrayList<>(); + Map<String, LfsPointer> oidStr2ptr = new HashMap<>(); + for (LfsPointer p : res) { + oidStr2ptr.put(p.getOid().name(), p); + } + HttpConnection lfsServerConn = LfsConnectionFactory.getLfsConnection(db, + HttpSupport.METHOD_POST, Protocol.OPERATION_DOWNLOAD); + Gson gson = Protocol.gson(); + lfsServerConn.getOutputStream() + .write(gson + .toJson(LfsConnectionFactory + .toRequest(Protocol.OPERATION_DOWNLOAD, res)) + .getBytes(StandardCharsets.UTF_8)); + int responseCode = lfsServerConn.getResponseCode(); + if (responseCode != HttpConnection.HTTP_OK) { + throw new IOException( + MessageFormat.format(LfsText.get().serverFailure, + lfsServerConn.getURL(), + Integer.valueOf(responseCode))); + } + try (JsonReader reader = new JsonReader( + new InputStreamReader(lfsServerConn.getInputStream()))) { + Protocol.Response resp = gson.fromJson(reader, + Protocol.Response.class); + for (Protocol.ObjectInfo o : resp.objects) { + if (o.error != null) { + throw new IOException( + MessageFormat.format(LfsText.get().protocolError, + Integer.valueOf(o.error.code), + o.error.message)); + } + if (o.actions == null) { + continue; + } + LfsPointer ptr = oidStr2ptr.get(o.oid); + if (ptr == null) { + // received an object we didn't request + continue; + } + if (ptr.getSize() != o.size) { + throw new IOException(MessageFormat.format( + LfsText.get().inconsistentContentLength, + lfsServerConn.getURL(), Long.valueOf(ptr.getSize()), + Long.valueOf(o.size))); + } + Protocol.Action downloadAction = o.actions + .get(Protocol.OPERATION_DOWNLOAD); + if (downloadAction == null || downloadAction.href == null) { + continue; + } + + HttpConnection contentServerConn = LfsConnectionFactory + .getLfsContentConnection(db, downloadAction, + HttpSupport.METHOD_GET); + + responseCode = contentServerConn.getResponseCode(); + if (responseCode != HttpConnection.HTTP_OK) { + throw new IOException( + MessageFormat.format(LfsText.get().serverFailure, + contentServerConn.getURL(), + Integer.valueOf(responseCode))); + } + Path path = lfs.getMediaFile(ptr.getOid()); + path.getParent().toFile().mkdirs(); + try (InputStream contentIn = contentServerConn + .getInputStream()) { + long bytesCopied = Files.copy(contentIn, path); + if (bytesCopied != o.size) { + throw new IOException(MessageFormat.format( + LfsText.get().wrongAmoutOfDataReceived, + contentServerConn.getURL(), + Long.valueOf(bytesCopied), + Long.valueOf(o.size))); + } + downloadedPaths.add(path); + } + } + } + return downloadedPaths; + } + /** {@inheritDoc} */ @Override public int run() throws IOException { - int b; - if (in != null) { - while ((b = in.read()) != -1) { - out.write(b); + try { + int totalRead = 0; + int length = 0; + if (in != null) { + byte[] buf = new byte[8192]; + while ((length = in.read(buf)) != -1) { + out.write(buf, 0, length); + totalRead += length; + + // when threshold reached, loop back to the caller. + // otherwise we could only support files up to 2GB (int + // return type) properly. we will be called again as long as + // we don't return -1 here. + if (totalRead >= MAX_COPY_BYTES) { + // leave streams open - we need them in the next call. + return totalRead; + } + } + } + + if (totalRead == 0 && length == -1) { + // we're totally done :) cleanup all streams + in.close(); + out.close(); + return length; } - in.close(); + + return totalRead; + } catch (IOException e) { + in.close(); // clean up - we swapped this stream. + out.close(); + throw e; } - out.close(); - return -1; } + } diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsConfigInvalidException.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsConfigInvalidException.java new file mode 100644 index 0000000000..5320af0b78 --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsConfigInvalidException.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016, Christian Halstrick <christian.halstrick@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.errors; + +import java.io.IOException; + +/** + * Thrown when a LFS configuration problem has been detected (i.e. unable to + * find the remote LFS repository URL). + * + * @since 4.11 + */ +public class LfsConfigInvalidException extends IOException { + private static final long serialVersionUID = 1L; + + /** + * Constructor for LfsConfigInvalidException. + * + * @param msg + * the error description + */ + public LfsConfigInvalidException(String msg) { + super(msg); + } + +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java new file mode 100644 index 0000000000..5d8268b73e --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2017, Markus Duft <markus.duft@ssi-schaefer.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.internal; + +import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP; +import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT; +import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING; +import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.ProxySelector; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.LinkedList; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.lfs.LfsPointer; +import org.eclipse.jgit.lfs.Protocol; +import org.eclipse.jgit.lfs.errors.LfsConfigInvalidException; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.transport.HttpConfig; +import org.eclipse.jgit.transport.HttpTransport; +import org.eclipse.jgit.transport.RemoteSession; +import org.eclipse.jgit.transport.SshSessionFactory; +import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.transport.http.HttpConnection; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.HttpSupport; +import org.eclipse.jgit.util.io.MessageWriter; +import org.eclipse.jgit.util.io.StreamCopyThread; + +/** + * Provides means to get a valid LFS connection for a given repository. + */ +public class LfsConnectionFactory { + + private static final String SCHEME_HTTPS = "https"; //$NON-NLS-1$ + private static final String SCHEME_SSH = "ssh"; //$NON-NLS-1$ + private static final Map<String, AuthCache> sshAuthCache = new TreeMap<>(); + + /** + * Determine URL of LFS server by looking into config parameters lfs.url, + * lfs.[remote].url or remote.[remote].url. The LFS server URL is computed + * from remote.[remote].url by appending "/info/lfs". In case there is no + * URL configured, a SSH remote URI can be used to auto-detect the LFS URI + * by using the remote "git-lfs-authenticate" command. + * + * @param db + * the repository to work with + * @param method + * the method (GET,PUT,...) of the request this connection will + * be used for + * @param purpose + * the action, e.g. Protocol.OPERATION_DOWNLOAD + * @return the url for the lfs server. e.g. + * "https://github.com/github/git-lfs.git/info/lfs" + * @throws IOException + */ + public static HttpConnection getLfsConnection(Repository db, String method, + String purpose) throws IOException { + StoredConfig config = db.getConfig(); + Map<String, String> additionalHeaders = new TreeMap<>(); + String lfsUrl = getLfsUrl(db, purpose, additionalHeaders); + URL url = new URL(lfsUrl + Protocol.OBJECTS_LFS_ENDPOINT); + HttpConnection connection = HttpTransport.getConnectionFactory().create( + url, HttpSupport.proxyFor(ProxySelector.getDefault(), url)); + connection.setDoOutput(true); + if (url.getProtocol().equals(SCHEME_HTTPS) + && !config.getBoolean(HttpConfig.HTTP, + HttpConfig.SSL_VERIFY_KEY, true)) { + HttpSupport.disableSslVerify(connection); + } + connection.setRequestMethod(method); + connection.setRequestProperty(HDR_ACCEPT, + Protocol.CONTENTTYPE_VND_GIT_LFS_JSON); + connection.setRequestProperty(HDR_CONTENT_TYPE, + Protocol.CONTENTTYPE_VND_GIT_LFS_JSON); + additionalHeaders + .forEach((k, v) -> connection.setRequestProperty(k, v)); + return connection; + } + + private static String getLfsUrl(Repository db, String purpose, + Map<String, String> additionalHeaders) + throws LfsConfigInvalidException { + StoredConfig config = db.getConfig(); + String lfsUrl = config.getString(ConfigConstants.CONFIG_SECTION_LFS, + null, + ConfigConstants.CONFIG_KEY_URL); + if (lfsUrl == null) { + String remoteUrl = null; + for (String remote : db.getRemoteNames()) { + lfsUrl = config.getString(ConfigConstants.CONFIG_SECTION_LFS, + remote, + ConfigConstants.CONFIG_KEY_URL); + // This could be done better (more precise logic), but according + // to https://github.com/git-lfs/git-lfs/issues/1759 git-lfs + // generally only supports 'origin' in an integrated workflow. + if (lfsUrl == null && (remote.equals( + org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME))) { + remoteUrl = config.getString( + ConfigConstants.CONFIG_KEY_REMOTE, remote, + ConfigConstants.CONFIG_KEY_URL); + } + break; + } + if (lfsUrl == null && remoteUrl != null) { + lfsUrl = discoverLfsUrl(db, purpose, additionalHeaders, + remoteUrl); + } else { + lfsUrl = lfsUrl + Protocol.INFO_LFS_ENDPOINT; + } + } + if (lfsUrl == null) { + throw new LfsConfigInvalidException(LfsText.get().lfsNoDownloadUrl); + } + return lfsUrl; + } + + private static String discoverLfsUrl(Repository db, String purpose, + Map<String, String> additionalHeaders, String remoteUrl) { + try { + URIish u = new URIish(remoteUrl); + if (SCHEME_SSH.equals(u.getScheme())) { + Protocol.ExpiringAction action = getSshAuthentication( + db, purpose, remoteUrl, u); + additionalHeaders.putAll(action.header); + return action.href; + } else { + return remoteUrl + Protocol.INFO_LFS_ENDPOINT; + } + } catch (Exception e) { + return null; // could not discover + } + } + + private static Protocol.ExpiringAction getSshAuthentication( + Repository db, String purpose, String remoteUrl, URIish u) + throws IOException { + AuthCache cached = sshAuthCache.get(remoteUrl); + Protocol.ExpiringAction action = null; + if (cached != null && cached.validUntil > System.currentTimeMillis()) { + action = cached.cachedAction; + } + + if (action == null) { + // discover and authenticate; git-lfs does "ssh + // -p <port> -- <host> git-lfs-authenticate + // <project> <upload/download>" + String json = runSshCommand(u.setPath(""), //$NON-NLS-1$ + db.getFS(), + "git-lfs-authenticate " + extractProjectName(u) + " " //$NON-NLS-1$//$NON-NLS-2$ + + purpose); + + action = Protocol.gson().fromJson(json, + Protocol.ExpiringAction.class); + + // cache the result as long as possible. + AuthCache c = new AuthCache(action); + sshAuthCache.put(remoteUrl, c); + } + return action; + } + + /** + * Create a connection for the specified + * {@link org.eclipse.jgit.lfs.Protocol.Action}. + * + * @param repo + * the repo to fetch required configuration from + * @param action + * the action for which to create a connection + * @param method + * the target method (GET or PUT) + * @return a connection. output mode is not set. + * @throws IOException + * in case of any error. + */ + public static @NonNull HttpConnection getLfsContentConnection( + Repository repo, Protocol.Action action, String method) + throws IOException { + URL contentUrl = new URL(action.href); + HttpConnection contentServerConn = HttpTransport.getConnectionFactory() + .create(contentUrl, HttpSupport + .proxyFor(ProxySelector.getDefault(), contentUrl)); + contentServerConn.setRequestMethod(method); + action.header + .forEach((k, v) -> contentServerConn.setRequestProperty(k, v)); + if (contentUrl.getProtocol().equals(SCHEME_HTTPS) + && !repo.getConfig().getBoolean(HttpConfig.HTTP, + HttpConfig.SSL_VERIFY_KEY, true)) { + HttpSupport.disableSslVerify(contentServerConn); + } + + contentServerConn.setRequestProperty(HDR_ACCEPT_ENCODING, + ENCODING_GZIP); + + return contentServerConn; + } + + private static String extractProjectName(URIish u) { + String path = u.getPath().substring(1); + if (path.endsWith(org.eclipse.jgit.lib.Constants.DOT_GIT)) { + return path.substring(0, path.length() - 4); + } else { + return path; + } + } + + private static String runSshCommand(URIish sshUri, FS fs, String command) + throws IOException { + RemoteSession session = null; + Process process = null; + StreamCopyThread errorThread = null; + try (MessageWriter stderr = new MessageWriter()) { + session = SshSessionFactory.getInstance().getSession(sshUri, null, + fs, 5_000); + process = session.exec(command, 0); + errorThread = new StreamCopyThread(process.getErrorStream(), + stderr.getRawStream()); + errorThread.start(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream(), + org.eclipse.jgit.lib.Constants.CHARSET))) { + return reader.readLine(); + } + } finally { + if (process != null) { + process.destroy(); + } + if (errorThread != null) { + try { + errorThread.halt(); + } catch (InterruptedException e) { + // Stop waiting and return anyway. + } finally { + errorThread = null; + } + } + if (session != null) { + SshSessionFactory.getInstance().releaseSession(session); + } + } + } + + /** + * @param operation + * the operation to perform, e.g. Protocol.OPERATION_DOWNLOAD + * @param resources + * the LFS resources affected + * @return a request that can be serialized to JSON + */ + public static Protocol.Request toRequest(String operation, + LfsPointer... resources) { + Protocol.Request req = new Protocol.Request(); + req.operation = operation; + if (resources != null) { + req.objects = new LinkedList<>(); + for (LfsPointer res : resources) { + Protocol.ObjectSpec o = new Protocol.ObjectSpec(); + o.oid = res.getOid().getName(); + o.size = res.getSize(); + req.objects.add(o); + } + } + return req; + } + + private static final class AuthCache { + private static final long AUTH_CACHE_EAGER_TIMEOUT = 100; + + private static final SimpleDateFormat ISO_FORMAT = new SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss.SSSX"); //$NON-NLS-1$ + + /** + * Creates a cache entry for an authentication response. + * <p> + * The timeout of the cache token is extracted from the given action. If + * no timeout can be determined, the token will be used only once. + * + * @param action + */ + public AuthCache(Protocol.ExpiringAction action) { + this.cachedAction = action; + try { + if (action.expiresIn != null && !action.expiresIn.isEmpty()) { + this.validUntil = System.currentTimeMillis() + + Long.parseLong(action.expiresIn); + } else if (action.expiresAt != null + && !action.expiresAt.isEmpty()) { + this.validUntil = ISO_FORMAT.parse(action.expiresAt) + .getTime() - AUTH_CACHE_EAGER_TIMEOUT; + } else { + this.validUntil = System.currentTimeMillis(); + } + } catch (Exception e) { + this.validUntil = System.currentTimeMillis(); + } + } + + long validUntil; + + Protocol.ExpiringAction cachedAction; + } + +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java index ae5548c85c..d7d0fe186a 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java @@ -62,13 +62,20 @@ public class LfsText extends TranslationBundle { // @formatter:off /***/ public String corruptLongObject; /***/ public String inconsistentMediafileLength; + /***/ public String inconsistentContentLength; /***/ public String incorrectLONG_OBJECT_ID_LENGTH; /***/ public String invalidLongId; /***/ public String invalidLongIdLength; + /***/ public String lfsUnavailable; + /***/ public String protocolError; /***/ public String requiredHashFunctionNotAvailable; /***/ public String repositoryNotFound; /***/ public String repositoryReadOnly; - /***/ public String lfsUnavailable; /***/ public String lfsUnathorized; /***/ public String lfsFailedToGetRepository; + /***/ public String lfsNoDownloadUrl; + /***/ public String serverFailure; + /***/ public String wrongAmoutOfDataReceived; + /***/ public String userConfigInvalid; + /***/ public String missingLocalObject; } diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java index d5b96ab0fd..835d7be02b 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java @@ -56,7 +56,7 @@ import org.eclipse.jgit.lfs.internal.LfsText; @SuppressWarnings("nls") public final class Constants { /** - * lfs folder + * lfs folder/section/filter name * * @since 4.6 */ @@ -108,6 +108,13 @@ public final class Constants { public static final String VERIFY = "verify"; /** + * Prefix for all LFS related filters. + * + * @since 4.11 + */ + public static final String ATTR_FILTER_DRIVER_PREFIX = "lfs/"; + + /** * Create a new digest function for objects. * * @return a new digest object. diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml index e3f229a62d..db17979570 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit" label="%featureName" - version="4.10.1.qualifier" + version="4.11.2.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -79,4 +79,11 @@ version="0.0.0" unpack="false"/> + <plugin + id="com.jcraft.jzlib" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + </feature> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml index da36437852..da2ed4a94d 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.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 0d674404e1..34f57a7c4f 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.http.apache" label="%featureName" - version="4.10.1.qualifier" + version="4.11.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 06ce041545..a5bc52713f 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.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 0664991a9e..5edc86246c 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.junit" label="%featureName" - version="4.10.1.qualifier" + version="4.11.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 b0dc93c4e6..dae274bdd0 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml index 0cd35e089f..c0e9111631 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.lfs" label="%featureName" - version="4.10.1.qualifier" + version="4.11.2.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml index ee9b28f07f..00867ddc2f 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml index 7434e2cf17..2ec405f495 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.pgm" label="%featureName" - version="4.10.1.qualifier" + version="4.11.2.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -31,8 +31,8 @@ version="0.0.0"/> <requires> - <import feature="org.eclipse.jgit" version="4.10.1" match="equivalent"/> - <import feature="org.eclipse.jgit.lfs" version="4.10.1" match="equivalent"/> + <import feature="org.eclipse.jgit" version="4.11.2" match="equivalent"/> + <import feature="org.eclipse.jgit.lfs" version="4.11.2" 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 1695b26357..32b93e305a 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.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 14c2c1c8d4..e26f540b9f 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.pgm.source" label="%featureName" - version="4.10.1.qualifier" + version="4.11.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 073ae3266d..8a1a8ce85b 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml index e8ddf111bc..ff4bc743c2 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.repository</artifactId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml index 66daef8d5b..b47c911a83 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.source" label="%featureName" - version="4.10.1.qualifier" + version="4.11.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 fe3818a48f..b6ccdafdf8 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.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 b850297843..e6bd066b9e 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.10.1.qualifier +Bundle-Version: 4.11.2.qualifier diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target index e3d9e4d45f..0c1d5bb809 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <?pde?> <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform --> -<target name="jgit-4.5" sequenceNumber="1512295767"> +<target name="jgit-4.5" sequenceNumber="1535176347"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.jetty.client" version="9.4.8.v20171121"/> @@ -27,42 +27,45 @@ <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/> <unit id="org.apache.commons.codec" version="1.9.0.v20170208-1614"/> <unit id="org.apache.commons.codec.source" version="1.9.0.v20170208-1614"/> - <unit id="org.apache.commons.compress" version="1.6.0.v201310281400"/> - <unit id="org.apache.commons.compress.source" version="1.6.0.v201310281400"/> + <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/> + <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/> <unit id="org.apache.commons.logging" version="1.1.1.v201101211721"/> <unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/> <unit id="org.apache.httpcomponents.httpcore" version="4.4.6.v20170210-0925"/> <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.6.v20170210-0925"/> - <unit id="org.apache.httpcomponents.httpclient" version="4.5.2.v20170210-0925"/> - <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.2.v20170210-0925"/> + <unit id="org.apache.httpcomponents.httpclient" version="4.5.2.v20180410-1551"/> + <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.2.v20180410-1551"/> <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> - <unit id="org.hamcrest.core" version="1.3.0.v201303031735"/> - <unit id="org.hamcrest.core.source" version="1.3.0.v201303031735"/> - <unit id="org.hamcrest.library" version="1.3.0.v201505072020"/> - <unit id="org.hamcrest.library.source" version="1.3.0.v201505072020"/> + <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/> + <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/> + <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/> + <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/> <unit id="javaewah" version="1.1.6.v20160919-1400"/> <unit id="javaewah.source" version="1.1.6.v20160919-1400"/> <unit id="org.objenesis" version="1.0.0.v201505121915"/> <unit id="org.objenesis.source" version="1.0.0.v201505121915"/> <unit id="org.mockito" version="1.8.4.v201303031500"/> <unit id="org.mockito.source" version="1.8.4.v201303031500"/> - <unit id="com.google.gson" version="2.2.4.v201311231704"/> + <unit id="com.google.gson" version="2.8.2.v20180104-1110"/> + <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/> <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/> <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/> <unit id="org.junit" version="4.12.0.v201504281640"/> <unit id="org.junit.source" version="4.12.0.v201504281640"/> <unit id="javax.servlet" version="3.1.0.v201410161800"/> <unit id="javax.servlet.source" version="3.1.0.v201410161800"/> - <unit id="org.tukaani.xz" version="1.3.0.v201308270617"/> - <unit id="org.tukaani.xz.source" version="1.3.0.v201308270617"/> + <unit id="org.tukaani.xz" version="1.6.0.v20170629-1752"/> + <unit id="org.tukaani.xz.source" version="1.6.0.v20170629-1752"/> <unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/> <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/> <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/> <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/> - <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20170919201930/repository"/> + <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> + <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> + <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180606145124/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd index 75101f66a5..c85343c9f9 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd @@ -1,7 +1,7 @@ target "jgit-4.5" with source configurePhase include "projects/jetty-9.4.8.tpd" -include "orbit/R20170919201930-Oxygen.tpd" +include "orbit/R20180606145124-Photon.tpd" location "http://download.eclipse.org/releases/mars/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target index 4c7944110d..e05c410128 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <?pde?> <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform --> -<target name="jgit-4.6" sequenceNumber="1512295762"> +<target name="jgit-4.6" sequenceNumber="1535176335"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.jetty.client" version="9.4.8.v20171121"/> @@ -27,42 +27,45 @@ <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/> <unit id="org.apache.commons.codec" version="1.9.0.v20170208-1614"/> <unit id="org.apache.commons.codec.source" version="1.9.0.v20170208-1614"/> - <unit id="org.apache.commons.compress" version="1.6.0.v201310281400"/> - <unit id="org.apache.commons.compress.source" version="1.6.0.v201310281400"/> + <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/> + <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/> <unit id="org.apache.commons.logging" version="1.1.1.v201101211721"/> <unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/> <unit id="org.apache.httpcomponents.httpcore" version="4.4.6.v20170210-0925"/> <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.6.v20170210-0925"/> - <unit id="org.apache.httpcomponents.httpclient" version="4.5.2.v20170210-0925"/> - <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.2.v20170210-0925"/> + <unit id="org.apache.httpcomponents.httpclient" version="4.5.2.v20180410-1551"/> + <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.2.v20180410-1551"/> <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> - <unit id="org.hamcrest.core" version="1.3.0.v201303031735"/> - <unit id="org.hamcrest.core.source" version="1.3.0.v201303031735"/> - <unit id="org.hamcrest.library" version="1.3.0.v201505072020"/> - <unit id="org.hamcrest.library.source" version="1.3.0.v201505072020"/> + <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/> + <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/> + <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/> + <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/> <unit id="javaewah" version="1.1.6.v20160919-1400"/> <unit id="javaewah.source" version="1.1.6.v20160919-1400"/> <unit id="org.objenesis" version="1.0.0.v201505121915"/> <unit id="org.objenesis.source" version="1.0.0.v201505121915"/> <unit id="org.mockito" version="1.8.4.v201303031500"/> <unit id="org.mockito.source" version="1.8.4.v201303031500"/> - <unit id="com.google.gson" version="2.2.4.v201311231704"/> + <unit id="com.google.gson" version="2.8.2.v20180104-1110"/> + <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/> <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/> <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/> <unit id="org.junit" version="4.12.0.v201504281640"/> <unit id="org.junit.source" version="4.12.0.v201504281640"/> <unit id="javax.servlet" version="3.1.0.v201410161800"/> <unit id="javax.servlet.source" version="3.1.0.v201410161800"/> - <unit id="org.tukaani.xz" version="1.3.0.v201308270617"/> - <unit id="org.tukaani.xz.source" version="1.3.0.v201308270617"/> + <unit id="org.tukaani.xz" version="1.6.0.v20170629-1752"/> + <unit id="org.tukaani.xz.source" version="1.6.0.v20170629-1752"/> <unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/> <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/> <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/> <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/> - <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20170919201930/repository"/> + <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> + <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> + <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180606145124/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd index 7de58709fc..3c2a910f8d 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd @@ -1,7 +1,7 @@ target "jgit-4.6" with source configurePhase include "projects/jetty-9.4.8.tpd" -include "orbit/R20170919201930-Oxygen.tpd" +include "orbit/R20180606145124-Photon.tpd" location "http://download.eclipse.org/releases/neon/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target index 9b1c15bcb1..20b0a294a6 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <?pde?> <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform --> -<target name="jgit-4.7" sequenceNumber="1512295741"> +<target name="jgit-4.7" sequenceNumber="1535176314"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.jetty.client" version="9.4.8.v20171121"/> @@ -27,42 +27,45 @@ <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/> <unit id="org.apache.commons.codec" version="1.9.0.v20170208-1614"/> <unit id="org.apache.commons.codec.source" version="1.9.0.v20170208-1614"/> - <unit id="org.apache.commons.compress" version="1.6.0.v201310281400"/> - <unit id="org.apache.commons.compress.source" version="1.6.0.v201310281400"/> + <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/> + <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/> <unit id="org.apache.commons.logging" version="1.1.1.v201101211721"/> <unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/> <unit id="org.apache.httpcomponents.httpcore" version="4.4.6.v20170210-0925"/> <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.6.v20170210-0925"/> - <unit id="org.apache.httpcomponents.httpclient" version="4.5.2.v20170210-0925"/> - <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.2.v20170210-0925"/> + <unit id="org.apache.httpcomponents.httpclient" version="4.5.2.v20180410-1551"/> + <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.2.v20180410-1551"/> <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> - <unit id="org.hamcrest.core" version="1.3.0.v201303031735"/> - <unit id="org.hamcrest.core.source" version="1.3.0.v201303031735"/> - <unit id="org.hamcrest.library" version="1.3.0.v201505072020"/> - <unit id="org.hamcrest.library.source" version="1.3.0.v201505072020"/> + <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/> + <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/> + <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/> + <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/> <unit id="javaewah" version="1.1.6.v20160919-1400"/> <unit id="javaewah.source" version="1.1.6.v20160919-1400"/> <unit id="org.objenesis" version="1.0.0.v201505121915"/> <unit id="org.objenesis.source" version="1.0.0.v201505121915"/> <unit id="org.mockito" version="1.8.4.v201303031500"/> <unit id="org.mockito.source" version="1.8.4.v201303031500"/> - <unit id="com.google.gson" version="2.2.4.v201311231704"/> + <unit id="com.google.gson" version="2.8.2.v20180104-1110"/> + <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/> <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/> <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/> <unit id="org.junit" version="4.12.0.v201504281640"/> <unit id="org.junit.source" version="4.12.0.v201504281640"/> <unit id="javax.servlet" version="3.1.0.v201410161800"/> <unit id="javax.servlet.source" version="3.1.0.v201410161800"/> - <unit id="org.tukaani.xz" version="1.3.0.v201308270617"/> - <unit id="org.tukaani.xz.source" version="1.3.0.v201308270617"/> + <unit id="org.tukaani.xz" version="1.6.0.v20170629-1752"/> + <unit id="org.tukaani.xz.source" version="1.6.0.v20170629-1752"/> <unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/> <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/> <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/> <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/> - <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20170919201930/repository"/> + <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> + <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> + <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180606145124/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd index 1def86c6ae..4e543fffee 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd @@ -1,7 +1,7 @@ target "jgit-4.7" with source configurePhase include "projects/jetty-9.4.8.tpd" -include "orbit/R20170919201930-Oxygen.tpd" +include "orbit/R20180606145124-Photon.tpd" location "http://download.eclipse.org/releases/oxygen/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target index 5180e411b6..b84fc208f7 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <?pde?> <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform --> -<target name="jgit-4.8" sequenceNumber="1535124646"> +<target name="jgit-4.8" sequenceNumber="1535176301"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.jetty.client" version="9.4.8.v20171121"/> @@ -27,8 +27,8 @@ <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/> <unit id="org.apache.commons.codec" version="1.9.0.v20170208-1614"/> <unit id="org.apache.commons.codec.source" version="1.9.0.v20170208-1614"/> - <unit id="org.apache.commons.compress" version="1.6.0.v201310281400"/> - <unit id="org.apache.commons.compress.source" version="1.6.0.v201310281400"/> + <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/> + <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/> <unit id="org.apache.commons.logging" version="1.1.1.v201101211721"/> <unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/> <unit id="org.apache.httpcomponents.httpcore" version="4.4.6.v20170210-0925"/> @@ -49,19 +49,22 @@ <unit id="org.objenesis.source" version="1.0.0.v201505121915"/> <unit id="org.mockito" version="1.8.4.v201303031500"/> <unit id="org.mockito.source" version="1.8.4.v201303031500"/> - <unit id="com.google.gson" version="2.2.4.v201311231704"/> + <unit id="com.google.gson" version="2.8.2.v20180104-1110"/> + <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/> <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/> <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/> <unit id="org.junit" version="4.12.0.v201504281640"/> <unit id="org.junit.source" version="4.12.0.v201504281640"/> <unit id="javax.servlet" version="3.1.0.v201410161800"/> <unit id="javax.servlet.source" version="3.1.0.v201410161800"/> - <unit id="org.tukaani.xz" version="1.3.0.v201308270617"/> - <unit id="org.tukaani.xz.source" version="1.3.0.v201308270617"/> + <unit id="org.tukaani.xz" version="1.6.0.v20170629-1752"/> + <unit id="org.tukaani.xz.source" version="1.6.0.v20170629-1752"/> <unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/> <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/> <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/> <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/> + <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> + <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180606145124/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20170919201930-Oxygen.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180206163158-Oxygen.tpd index 4e9cc13f5c..91c66db123 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20170919201930-Oxygen.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180206163158-Oxygen.tpd @@ -1,7 +1,7 @@ -target "R20170919201930-Oxygen" with source configurePhase +target "R20180206163158-Oxygen" with source configurePhase // see http://download.eclipse.org/tools/orbit/downloads/ -location "http://download.eclipse.org/tools/orbit/downloads/drops/R20170919201930/repository" { +location "http://download.eclipse.org/tools/orbit/downloads/drops/R20180206163158/repository" { org.apache.ant [1.9.6.v201510161327,1.9.6.v201510161327] org.apache.ant.source [1.9.6.v201510161327,1.9.6.v201510161327] org.apache.commons.codec [1.9.0.v20170208-1614,1.9.0.v20170208-1614] @@ -41,4 +41,6 @@ location "http://download.eclipse.org/tools/orbit/downloads/drops/R2017091920193 org.slf4j.api.source [1.7.2.v20121108-1250,1.7.2.v20121108-1250] org.slf4j.impl.log4j12 [1.7.2.v20131105-2200,1.7.2.v20131105-2200] org.slf4j.impl.log4j12.source [1.7.2.v20131105-2200,1.7.2.v20131105-2200] + com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305] + com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305] }
\ No newline at end of file diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180606145124-Photon.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180606145124-Photon.tpd index 86c888a40f..f4ec50dc79 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180606145124-Photon.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180606145124-Photon.tpd @@ -6,8 +6,8 @@ location "http://download.eclipse.org/tools/orbit/downloads/drops/R2018060614512 org.apache.ant.source [1.9.6.v201510161327,1.9.6.v201510161327] org.apache.commons.codec [1.9.0.v20170208-1614,1.9.0.v20170208-1614] org.apache.commons.codec.source [1.9.0.v20170208-1614,1.9.0.v20170208-1614] - org.apache.commons.compress [1.6.0.v201310281400,1.6.0.v201310281400] - org.apache.commons.compress.source [1.6.0.v201310281400,1.6.0.v201310281400] + org.apache.commons.compress [1.15.0.v20180119-1613,1.15.0.v20180119-1613] + org.apache.commons.compress.source [1.15.0.v20180119-1613,1.15.0.v20180119-1613s] org.apache.commons.logging [1.1.1.v201101211721,1.1.1.v201101211721] org.apache.commons.logging.source [1.1.1.v201101211721,1.1.1.v201101211721] org.apache.httpcomponents.httpcore [4.4.6.v20170210-0925,4.4.6.v20170210-0925] @@ -28,17 +28,20 @@ location "http://download.eclipse.org/tools/orbit/downloads/drops/R2018060614512 org.objenesis.source [1.0.0.v201505121915,1.0.0.v201505121915] org.mockito [1.8.4.v201303031500,1.8.4.v201303031500] org.mockito.source [1.8.4.v201303031500,1.8.4.v201303031500] - com.google.gson [2.2.4.v201311231704,2.2.4.v201311231704] + com.google.gson [2.8.2.v20180104-1110,2.8.2.v20180104-1110] + com.google.gson.source [2.8.2.v20180104-1110,2.8.2.v20180104-1110] com.jcraft.jsch [0.1.54.v20170116-1932,0.1.54.v20170116-1932] com.jcraft.jsch.source [0.1.54.v20170116-1932,0.1.54.v20170116-1932] org.junit [4.12.0.v201504281640,4.12.0.v201504281640] org.junit.source [4.12.0.v201504281640,4.12.0.v201504281640] javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800] javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800] - org.tukaani.xz [1.3.0.v201308270617,1.3.0.v201308270617] - org.tukaani.xz.source [1.3.0.v201308270617,1.3.0.v201308270617] + org.tukaani.xz [1.6.0.v20170629-1752,1.6.0.v20170629-1752] + org.tukaani.xz.source [1.6.0.v20170629-1752,1.6.0.v20170629-1752] org.slf4j.api [1.7.2.v20121108-1250,1.7.2.v20121108-1250] org.slf4j.api.source [1.7.2.v20121108-1250,1.7.2.v20121108-1250] org.slf4j.impl.log4j12 [1.7.2.v20131105-2200,1.7.2.v20131105-2200] org.slf4j.impl.log4j12.source [1.7.2.v20131105-2200,1.7.2.v20131105-2200] + com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305] + com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305] }
\ No newline at end of file diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml index 2d26e792e7..c53da9ae53 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml @@ -49,7 +49,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.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 e12a21221d..86efc59fbf 100644 --- a/org.eclipse.jgit.packaging/pom.xml +++ b/org.eclipse.jgit.packaging/pom.xml @@ -53,13 +53,13 @@ <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> <packaging>pom</packaging> <name>JGit Tycho Parent</name> <properties> - <tycho-version>1.0.0</tycho-version> + <tycho-version>1.1.0</tycho-version> <tycho-extras-version>${tycho-version}</tycho-extras-version> <target-platform>jgit-4.6</target-platform> </properties> diff --git a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs index 64f74989e1..794592dee1 100644 --- a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF index 5518017bb8..03fdffe799 100644 --- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF @@ -3,28 +3,28 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.pgm.test Bundle-SymbolicName: org.eclipse.jgit.pgm.test -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: org.eclipse.jgit.api;version="[4.10.1,4.11.0)", - org.eclipse.jgit.api.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.diff;version="[4.10.1,4.11.0)", - org.eclipse.jgit.dircache;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.file;version="4.10.1", - org.eclipse.jgit.junit;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.merge;version="[4.10.1,4.11.0)", - org.eclipse.jgit.pgm;version="[4.10.1,4.11.0)", - org.eclipse.jgit.pgm.internal;version="[4.10.1,4.11.0)", - org.eclipse.jgit.pgm.opt;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport;version="[4.10.1,4.11.0)", - org.eclipse.jgit.treewalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util.io;version="[4.10.1,4.11.0)", +Import-Package: org.eclipse.jgit.api;version="[4.11.2,4.12.0)", + org.eclipse.jgit.api.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.diff;version="[4.11.2,4.12.0)", + org.eclipse.jgit.dircache;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.file;version="4.11.2", + org.eclipse.jgit.junit;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.merge;version="[4.11.2,4.12.0)", + org.eclipse.jgit.pgm;version="[4.11.2,4.12.0)", + org.eclipse.jgit.pgm.internal;version="[4.11.2,4.12.0)", + org.eclipse.jgit.pgm.opt;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util.io;version="[4.11.2,4.12.0)", org.hamcrest.core;bundle-version="[1.1.0,2.0.0)", org.junit;version="[4.12,5.0.0)", org.junit.rules;version="[4.12,5.0.0)", diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml index f4d6171689..1ac8cb9658 100644 --- a/org.eclipse.jgit.pgm.test/pom.xml +++ b/org.eclipse.jgit.pgm.test/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.pgm.test</artifactId> @@ -84,6 +84,11 @@ <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.tukaani</groupId> + <artifactId>xz</artifactId> + <optional>true</optional> + </dependency> </dependencies> <build> diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java index 3b352ce2aa..e5c85ebfc7 100644 --- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java @@ -42,6 +42,7 @@ */ package org.eclipse.jgit.pgm; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -611,7 +612,7 @@ public class ArchiveTest extends CLIRepositoryTestCase { private BufferedReader readFromProcess(Process proc) throws Exception { return new BufferedReader( - new InputStreamReader(proc.getInputStream(), "UTF-8")); + new InputStreamReader(proc.getInputStream(), UTF_8)); } private void grepForEntry(String name, String mode, String... cmdline) @@ -633,9 +634,8 @@ public class ArchiveTest extends CLIRepositoryTestCase { } private void assertMagic(long offset, byte[] magicBytes, File file) throws Exception { - BufferedInputStream in = new BufferedInputStream( - new FileInputStream(file)); - try { + try (BufferedInputStream in = new BufferedInputStream( + new FileInputStream(file))) { if (offset > 0) { long skipped = in.skip(offset); assertEquals(offset, skipped); @@ -644,8 +644,6 @@ public class ArchiveTest extends CLIRepositoryTestCase { byte[] actual = new byte[magicBytes.length]; in.read(actual); assertArrayEquals(magicBytes, actual); - } finally { - in.close(); } } @@ -686,23 +684,19 @@ public class ArchiveTest extends CLIRepositoryTestCase { private void writeRaw(String filename, byte[] data) throws IOException { File path = new File(db.getWorkTree(), filename); - OutputStream out = new FileOutputStream(path); - try { + try (OutputStream out = new FileOutputStream(path)) { out.write(data); - } finally { - out.close(); } } private static String[] listZipEntries(byte[] zipData) throws IOException { List<String> l = new ArrayList<>(); - ZipInputStream in = new ZipInputStream( - new ByteArrayInputStream(zipData)); - - ZipEntry e; - while ((e = in.getNextEntry()) != null) - l.add(e.getName()); - in.close(); + try (ZipInputStream in = new ZipInputStream( + new ByteArrayInputStream(zipData))) { + ZipEntry e; + while ((e = in.getNextEntry()) != null) + l.add(e.getName()); + } return l.toArray(new String[l.size()]); } @@ -725,22 +719,22 @@ public class ArchiveTest extends CLIRepositoryTestCase { private String[] listTarEntries(byte[] tarData) throws Exception { List<String> l = new ArrayList<>(); Process proc = spawnAssumingCommandPresent("tar", "tf", "-"); - BufferedReader reader = readFromProcess(proc); - OutputStream out = proc.getOutputStream(); + try (BufferedReader reader = readFromProcess(proc)) { + OutputStream out = proc.getOutputStream(); - // Dump tarball to tar stdin in background - Future<?> writing = writeAsync(out, tarData); + // Dump tarball to tar stdin in background + Future<?> writing = writeAsync(out, tarData); - try { - String line; - while ((line = reader.readLine()) != null) - l.add(line); + try { + String line; + while ((line = reader.readLine()) != null) + l.add(line); - return l.toArray(new String[l.size()]); - } finally { - writing.get(); - reader.close(); - proc.destroy(); + return l.toArray(new String[l.size()]); + } finally { + writing.get(); + proc.destroy(); + } } } @@ -756,7 +750,7 @@ public class ArchiveTest extends CLIRepositoryTestCase { // found! List<String> l = new ArrayList<>(); BufferedReader reader = new BufferedReader( - new InputStreamReader(in, "UTF-8")); + new InputStreamReader(in, UTF_8)); String line; while ((line = reader.readLine()) != null) l.add(line); @@ -771,20 +765,20 @@ public class ArchiveTest extends CLIRepositoryTestCase { throws Exception { List<String> l = new ArrayList<>(); Process proc = spawnAssumingCommandPresent("tar", "Oxf", "-", path); - BufferedReader reader = readFromProcess(proc); - OutputStream out = proc.getOutputStream(); - Future<?> writing = writeAsync(out, tarData); - - try { - String line; - while ((line = reader.readLine()) != null) - l.add(line); - - return l.toArray(new String[l.size()]); - } finally { - writing.get(); - reader.close(); - proc.destroy(); + try (BufferedReader reader = readFromProcess(proc)) { + OutputStream out = proc.getOutputStream(); + Future<?> writing = writeAsync(out, tarData); + + try { + String line; + while ((line = reader.readLine()) != null) + l.add(line); + + return l.toArray(new String[l.size()]); + } finally { + writing.get(); + proc.destroy(); + } } } } diff --git a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs index 06ddbabb48..13c32a6d94 100644 --- a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF index d1e2a3f23e..c23cd5bd07 100644 --- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.pgm Bundle-SymbolicName: org.eclipse.jgit.pgm -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-Localization: plugin @@ -28,49 +28,49 @@ Import-Package: javax.servlet;version="[3.1.0,4.0.0)", org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)", - org.eclipse.jgit.api;version="[4.10.1,4.11.0)", - org.eclipse.jgit.api.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.archive;version="[4.10.1,4.11.0)", - org.eclipse.jgit.awtui;version="[4.10.1,4.11.0)", - org.eclipse.jgit.blame;version="[4.10.1,4.11.0)", - org.eclipse.jgit.diff;version="[4.10.1,4.11.0)", - org.eclipse.jgit.dircache;version="[4.10.1,4.11.0)", - org.eclipse.jgit.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.gitrepo;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.ketch;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.dfs;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.io;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.pack;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.reftable;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.reftree;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs.server;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs.server.fs;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs.server.s3;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.merge;version="[4.10.1,4.11.0)", - org.eclipse.jgit.nls;version="[4.10.1,4.11.0)", - org.eclipse.jgit.notes;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revplot;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revwalk.filter;version="[4.10.1,4.11.0)", - org.eclipse.jgit.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.storage.pack;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport.http.apache;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport.resolver;version="[4.10.1,4.11.0)", - org.eclipse.jgit.treewalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.treewalk.filter;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util.io;version="[4.10.1,4.11.0)", + org.eclipse.jgit.api;version="[4.11.2,4.12.0)", + org.eclipse.jgit.api.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.archive;version="[4.11.2,4.12.0)", + org.eclipse.jgit.awtui;version="[4.11.2,4.12.0)", + org.eclipse.jgit.blame;version="[4.11.2,4.12.0)", + org.eclipse.jgit.diff;version="[4.11.2,4.12.0)", + org.eclipse.jgit.dircache;version="[4.11.2,4.12.0)", + org.eclipse.jgit.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.gitrepo;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.ketch;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.dfs;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.io;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.pack;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.reftable;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.reftree;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.server;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.server.fs;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs.server.s3;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.merge;version="[4.11.2,4.12.0)", + org.eclipse.jgit.nls;version="[4.11.2,4.12.0)", + org.eclipse.jgit.notes;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revplot;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk.filter;version="[4.11.2,4.12.0)", + org.eclipse.jgit.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.storage.pack;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.http.apache;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.resolver;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk.filter;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util.io;version="[4.11.2,4.12.0)", org.kohsuke.args4j;version="[2.33.0,3.0.0)", org.kohsuke.args4j.spi;version="[2.33.0,3.0.0)" -Export-Package: org.eclipse.jgit.console;version="4.10.1"; +Export-Package: org.eclipse.jgit.console;version="4.11.2"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util", - org.eclipse.jgit.pgm;version="4.10.1"; + org.eclipse.jgit.pgm;version="4.11.2"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.pgm.opt, @@ -81,11 +81,11 @@ Export-Package: org.eclipse.jgit.console;version="4.10.1"; org.eclipse.jgit.treewalk, javax.swing, org.eclipse.jgit.transport", - org.eclipse.jgit.pgm.debug;version="4.10.1"; + org.eclipse.jgit.pgm.debug;version="4.11.2"; uses:="org.eclipse.jgit.util.io, org.eclipse.jgit.pgm", - org.eclipse.jgit.pgm.internal;version="4.10.1";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test", - org.eclipse.jgit.pgm.opt;version="4.10.1"; + org.eclipse.jgit.pgm.internal;version="4.11.2";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test", + org.eclipse.jgit.pgm.opt;version="4.11.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 7d8d4baaea..5e2a62d4b3 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.10.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.10.1.qualifier";roots="." +Bundle-Version: 4.11.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.11.2.qualifier";roots="." diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml index 165733e698..21fbf41bfd 100644 --- a/org.eclipse.jgit.pgm/pom.xml +++ b/org.eclipse.jgit.pgm/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.pgm</artifactId> diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AmazonS3Client.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AmazonS3Client.java index 086c9766ac..cce889b76e 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AmazonS3Client.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AmazonS3Client.java @@ -89,8 +89,7 @@ class AmazonS3Client extends TextBuiltin { if ("get".equals(op)) { //$NON-NLS-1$ final URLConnection c = s3.get(bucket, key); int len = c.getContentLength(); - final InputStream in = c.getInputStream(); - try { + try (InputStream in = c.getInputStream()) { outw.flush(); final byte[] tmp = new byte[2048]; while (len > 0) { @@ -103,8 +102,6 @@ class AmazonS3Client extends TextBuiltin { len -= n; } outs.flush(); - } finally { - in.close(); } } else if ("ls".equals(op) || "list".equals(op)) { //$NON-NLS-1$//$NON-NLS-2$ @@ -115,13 +112,12 @@ class AmazonS3Client extends TextBuiltin { s3.delete(bucket, key); } else if ("put".equals(op)) { //$NON-NLS-1$ - final OutputStream os = s3.beginPut(bucket, key, null, null); - final byte[] tmp = new byte[2048]; - int n; - while ((n = ins.read(tmp)) > 0) - os.write(tmp, 0, n); - os.close(); - + try (OutputStream os = s3.beginPut(bucket, key, null, null)) { + final byte[] tmp = new byte[2048]; + int n; + while ((n = ins.read(tmp)) > 0) + os.write(tmp, 0, n); + } } else { throw die(MessageFormat.format(CLIText.get().unsupportedOperation, op)); } @@ -129,13 +125,10 @@ class AmazonS3Client extends TextBuiltin { private Properties properties() { try { - final InputStream in = new FileInputStream(propertyFile); - try { + try (InputStream in = new FileInputStream(propertyFile)) { final Properties p = new Properties(); p.load(in); return p; - } finally { - in.close(); } } catch (FileNotFoundException e) { throw die(MessageFormat.format(CLIText.get().noSuchFile, propertyFile), e); diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java index 1f80301f66..6ff39fab04 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java @@ -57,6 +57,7 @@ import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.TextProgressMonitor; import org.eclipse.jgit.pgm.internal.CLIText; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; @@ -90,7 +91,8 @@ class Checkout extends TextBuiltin { } try (Git git = new Git(db)) { - CheckoutCommand command = git.checkout(); + CheckoutCommand command = git.checkout() + .setProgressMonitor(new TextProgressMonitor(errw)); if (paths.size() > 0) { command.setStartPoint(name); if (paths.size() == 1 && paths.get(0).equals(".")) { //$NON-NLS-1$ diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java index 7bab69210b..81aeef8cdd 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java @@ -43,6 +43,8 @@ package org.eclipse.jgit.pgm; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -149,7 +151,7 @@ public class CommandCatalog { final BufferedReader cIn; try { final InputStream in = cUrl.openStream(); - cIn = new BufferedReader(new InputStreamReader(in, "UTF-8")); //$NON-NLS-1$ + cIn = new BufferedReader(new InputStreamReader(in, UTF_8)); } catch (IOException err) { // If we cannot read from the service list, go to the next. // diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java index b29b097473..a376bc098d 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java @@ -65,8 +65,7 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jgit.awtui.AwtAuthenticator; import org.eclipse.jgit.awtui.AwtCredentialsProvider; import org.eclipse.jgit.errors.TransportException; -import org.eclipse.jgit.lfs.CleanFilter; -import org.eclipse.jgit.lfs.SmudgeFilter; +import org.eclipse.jgit.lfs.BuiltinLFS; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryBuilder; import org.eclipse.jgit.pgm.internal.CLIText; @@ -111,8 +110,7 @@ public class Main { */ public Main() { HttpTransport.setConnectionFactory(new HttpClientConnectionFactory()); - CleanFilter.register(); - SmudgeFilter.register(); + BuiltinLFS.register(); gcExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() { private final ThreadFactory baseFactory = Executors .defaultThreadFactory(); @@ -134,6 +132,9 @@ public class Main { * @throws java.lang.Exception */ public static void main(final String[] argv) throws Exception { + // make sure built-in filters are registered + BuiltinLFS.register(); + new Main().run(argv); } diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java index 5f8ebdb1a9..0e1b398a73 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java @@ -160,11 +160,8 @@ class DiffAlgorithms extends TextBuiltin { else rb.findGitDir(dir); - Repository repo = rb.build(); - try { + try (Repository repo = rb.build()) { run(repo); - } finally { - repo.close(); } } } diff --git a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs index 64f74989e1..794592dee1 100644 --- a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index c215c9048f..d8877fd826 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF @@ -3,63 +3,63 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.test Bundle-SymbolicName: org.eclipse.jgit.test -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)", com.jcraft.jsch;version="[0.1.54,0.2.0)", - org.eclipse.jgit.api;version="[4.10.1,4.11.0)", - org.eclipse.jgit.api.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.attributes;version="[4.10.1,4.11.0)", - org.eclipse.jgit.awtui;version="[4.10.1,4.11.0)", - org.eclipse.jgit.blame;version="[4.10.1,4.11.0)", - org.eclipse.jgit.diff;version="[4.10.1,4.11.0)", - org.eclipse.jgit.dircache;version="[4.10.1,4.11.0)", - org.eclipse.jgit.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.events;version="[4.10.1,4.11.0)", - org.eclipse.jgit.fnmatch;version="[4.10.1,4.11.0)", - org.eclipse.jgit.gitrepo;version="[4.10.1,4.11.0)", - org.eclipse.jgit.hooks;version="[4.10.1,4.11.0)", - org.eclipse.jgit.ignore;version="[4.10.1,4.11.0)", - org.eclipse.jgit.ignore.internal;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.fsck;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.dfs;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.io;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.pack;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.reftable;version="[4.10.1,4.11.0)", - org.eclipse.jgit.internal.storage.reftree;version="[4.10.1,4.11.0)", - org.eclipse.jgit.junit;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lfs;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.merge;version="[4.10.1,4.11.0)", - org.eclipse.jgit.nls;version="[4.10.1,4.11.0)", - org.eclipse.jgit.notes;version="[4.10.1,4.11.0)", - org.eclipse.jgit.patch;version="[4.10.1,4.11.0)", - org.eclipse.jgit.pgm;version="[4.10.1,4.11.0)", - org.eclipse.jgit.pgm.internal;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revplot;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revwalk.filter;version="[4.10.1,4.11.0)", - org.eclipse.jgit.storage.file;version="[4.10.1,4.11.0)", - org.eclipse.jgit.storage.pack;version="[4.10.1,4.11.0)", - org.eclipse.jgit.submodule;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport.http;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport.resolver;version="[4.10.1,4.11.0)", - org.eclipse.jgit.treewalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.treewalk.filter;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util.io;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util.sha1;version="[4.10.1,4.11.0)", + org.eclipse.jgit.api;version="[4.11.2,4.12.0)", + org.eclipse.jgit.api.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.attributes;version="[4.11.2,4.12.0)", + org.eclipse.jgit.awtui;version="[4.11.2,4.12.0)", + org.eclipse.jgit.blame;version="[4.11.2,4.12.0)", + org.eclipse.jgit.diff;version="[4.11.2,4.12.0)", + org.eclipse.jgit.dircache;version="[4.11.2,4.12.0)", + org.eclipse.jgit.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.events;version="[4.11.2,4.12.0)", + org.eclipse.jgit.fnmatch;version="[4.11.2,4.12.0)", + org.eclipse.jgit.gitrepo;version="[4.11.2,4.12.0)", + org.eclipse.jgit.hooks;version="[4.11.2,4.12.0)", + org.eclipse.jgit.ignore;version="[4.11.2,4.12.0)", + org.eclipse.jgit.ignore.internal;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.fsck;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.dfs;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.io;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.pack;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.reftable;version="[4.11.2,4.12.0)", + org.eclipse.jgit.internal.storage.reftree;version="[4.11.2,4.12.0)", + org.eclipse.jgit.junit;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lfs;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.merge;version="[4.11.2,4.12.0)", + org.eclipse.jgit.nls;version="[4.11.2,4.12.0)", + org.eclipse.jgit.notes;version="[4.11.2,4.12.0)", + org.eclipse.jgit.patch;version="[4.11.2,4.12.0)", + org.eclipse.jgit.pgm;version="[4.11.2,4.12.0)", + org.eclipse.jgit.pgm.internal;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revplot;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk.filter;version="[4.11.2,4.12.0)", + org.eclipse.jgit.storage.file;version="[4.11.2,4.12.0)", + org.eclipse.jgit.storage.pack;version="[4.11.2,4.12.0)", + org.eclipse.jgit.submodule;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.http;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport.resolver;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.treewalk.filter;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util.io;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util.sha1;version="[4.11.2,4.12.0)", org.junit;version="[4.12,5.0.0)", org.junit.experimental.theories;version="[4.12,5.0.0)", org.junit.rules;version="[4.12,5.0.0)", org.junit.runner;version="[4.12,5.0.0)", org.junit.runners;version="[4.12,5.0.0)", - org.slf4j;version="[1.7.2,2.0.0)" + org.slf4j;version="[1.7.0,2.0.0)" Require-Bundle: org.hamcrest.core;bundle-version="[1.1.0,2.0.0)", org.hamcrest.library;bundle-version="[1.1.0,2.0.0)" diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/ignore/CGitVsJGitRandomIgnorePatternTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/ignore/CGitVsJGitRandomIgnorePatternTest.java index 03b34acc05..438d2d625b 100644 --- a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/ignore/CGitVsJGitRandomIgnorePatternTest.java +++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/ignore/CGitVsJGitRandomIgnorePatternTest.java @@ -187,10 +187,10 @@ public class CGitVsJGitRandomIgnorePatternTest { "--no-index", "-v", "-n", "--stdin" }; Process proc = Runtime.getRuntime().exec(command, new String[0], gitDir); - OutputStream out = proc.getOutputStream(); - out.write((path + "\n").getBytes(UTF_8)); - out.flush(); - out.close(); + try (OutputStream out = proc.getOutputStream()) { + out.write((path + "\n").getBytes(UTF_8)); + out.flush(); + } return proc; } diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java index 586505cfe8..3f9ef12507 100644 --- a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java +++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java @@ -202,28 +202,28 @@ public class EGitPatchHistoryTest { } void read() throws IOException, InterruptedException { - final BufferedReader in = new BufferedReader(new InputStreamReader( - proc.getInputStream(), "ISO-8859-1")); - String commitId = null; - TemporaryBuffer buf = null; - for (;;) { - String line = in.readLine(); - if (line == null) - break; - if (line.startsWith("commit ")) { - if (buf != null) { - buf.close(); - onCommit(commitId, buf.toByteArray()); - buf.destroy(); + try (BufferedReader in = new BufferedReader( + new InputStreamReader(proc.getInputStream(), ISO_8859_1))) { + String commitId = null; + TemporaryBuffer buf = null; + for (;;) { + String line = in.readLine(); + if (line == null) + break; + if (line.startsWith("commit ")) { + if (buf != null) { + buf.close(); + onCommit(commitId, buf.toByteArray()); + buf.destroy(); + } + commitId = line.substring("commit ".length()); + buf = new TemporaryBuffer.LocalFile(null); + } else if (buf != null) { + buf.write(line.getBytes(ISO_8859_1)); + buf.write('\n'); } - commitId = line.substring("commit ".length()); - buf = new TemporaryBuffer.LocalFile(null); - } else if (buf != null) { - buf.write(line.getBytes(ISO_8859_1)); - buf.write('\n'); } } - in.close(); assertEquals(0, proc.waitFor()); proc = null; } diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml index 900af567c0..18bc2b2ddb 100644 --- a/org.eclipse.jgit.test/pom.xml +++ b/org.eclipse.jgit.test/pom.xml @@ -52,7 +52,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.test</artifactId> diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java index aafda0171c..911f659149 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java @@ -63,8 +63,7 @@ import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; -import org.eclipse.jgit.lfs.CleanFilter; -import org.eclipse.jgit.lfs.SmudgeFilter; +import org.eclipse.jgit.lfs.BuiltinLFS; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; @@ -92,8 +91,7 @@ public class AddCommandTest extends RepositoryTestCase { @Override public void setUp() throws Exception { - CleanFilter.register(); - SmudgeFilter.register(); + BuiltinLFS.register(); super.setUp(); } @@ -120,9 +118,9 @@ public class AddCommandTest extends RepositoryTestCase { public void testAddExistingSingleFile() throws IOException, GitAPIException { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } try (Git git = new Git(db)) { git.add().addFilepattern("a.txt").call(); @@ -491,9 +489,9 @@ public class AddCommandTest extends RepositoryTestCase { GitAPIException { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("row1\r\nrow2"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("row1\r\nrow2"); + } try (Git git = new Git(db)) { db.getConfig().setString("core", null, "autocrlf", "false"); @@ -521,9 +519,9 @@ public class AddCommandTest extends RepositoryTestCase { data.append("row1\r\nrow2"); } String crData = data.toString(); - PrintWriter writer = new PrintWriter(file); - writer.print(crData); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print(crData); + } String lfData = data.toString().replaceAll("\r", ""); try (Git git = new Git(db)) { db.getConfig().setString("core", null, "autocrlf", "false"); @@ -546,9 +544,9 @@ public class AddCommandTest extends RepositoryTestCase { GitAPIException { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("row1\r\nrow2\u0000"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("row1\r\nrow2\u0000"); + } try (Git git = new Git(db)) { db.getConfig().setString("core", null, "autocrlf", "false"); @@ -572,9 +570,9 @@ public class AddCommandTest extends RepositoryTestCase { FileUtils.mkdir(new File(db.getWorkTree(), "sub")); File file = new File(db.getWorkTree(), "sub/a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } try (Git git = new Git(db)) { git.add().addFilepattern("sub/a.txt").call(); @@ -590,18 +588,18 @@ public class AddCommandTest extends RepositoryTestCase { GitAPIException { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } try (Git git = new Git(db)) { DirCache dc = git.add().addFilepattern("a.txt").call(); dc.getEntry(0).getObjectId(); - writer = new PrintWriter(file); - writer.print("other content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("other content"); + } dc = git.add().addFilepattern("a.txt").call(); @@ -615,9 +613,9 @@ public class AddCommandTest extends RepositoryTestCase { public void testAddExistingSingleFileTwiceWithCommit() throws Exception { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } try (Git git = new Git(db)) { DirCache dc = git.add().addFilepattern("a.txt").call(); @@ -626,9 +624,9 @@ public class AddCommandTest extends RepositoryTestCase { git.commit().setMessage("commit a.txt").call(); - writer = new PrintWriter(file); - writer.print("other content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("other content"); + } dc = git.add().addFilepattern("a.txt").call(); @@ -642,9 +640,9 @@ public class AddCommandTest extends RepositoryTestCase { public void testAddRemovedFile() throws Exception { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } try (Git git = new Git(db)) { DirCache dc = git.add().addFilepattern("a.txt").call(); @@ -665,9 +663,9 @@ public class AddCommandTest extends RepositoryTestCase { public void testAddRemovedCommittedFile() throws Exception { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } try (Git git = new Git(db)) { DirCache dc = git.add().addFilepattern("a.txt").call(); @@ -692,15 +690,15 @@ public class AddCommandTest extends RepositoryTestCase { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } File file2 = new File(db.getWorkTree(), "b.txt"); FileUtils.createNewFile(file2); - writer = new PrintWriter(file2); - writer.print("content b"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file2)) { + writer.print("content b"); + } ObjectInserter newObjectInserter = db.newObjectInserter(); DirCache dc = db.lockDirCache(); @@ -709,14 +707,14 @@ public class AddCommandTest extends RepositoryTestCase { addEntryToBuilder("b.txt", file2, newObjectInserter, builder, 0); addEntryToBuilder("a.txt", file, newObjectInserter, builder, 1); - writer = new PrintWriter(file); - writer.print("other content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("other content"); + } addEntryToBuilder("a.txt", file, newObjectInserter, builder, 3); - writer = new PrintWriter(file); - writer.print("our content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("our content"); + } addEntryToBuilder("a.txt", file, newObjectInserter, builder, 2) .getObjectId(); @@ -745,15 +743,15 @@ public class AddCommandTest extends RepositoryTestCase { public void testAddTwoFiles() throws Exception { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } File file2 = new File(db.getWorkTree(), "b.txt"); FileUtils.createNewFile(file2); - writer = new PrintWriter(file2); - writer.print("content b"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file2)) { + writer.print("content b"); + } try (Git git = new Git(db)) { git.add().addFilepattern("a.txt").addFilepattern("b.txt").call(); @@ -769,15 +767,15 @@ public class AddCommandTest extends RepositoryTestCase { FileUtils.mkdir(new File(db.getWorkTree(), "sub")); File file = new File(db.getWorkTree(), "sub/a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } File file2 = new File(db.getWorkTree(), "sub/b.txt"); FileUtils.createNewFile(file2); - writer = new PrintWriter(file2); - writer.print("content b"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file2)) { + writer.print("content b"); + } try (Git git = new Git(db)) { git.add().addFilepattern("sub").call(); @@ -793,21 +791,21 @@ public class AddCommandTest extends RepositoryTestCase { FileUtils.mkdir(new File(db.getWorkTree(), "sub")); File file = new File(db.getWorkTree(), "sub/a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } File ignoreFile = new File(db.getWorkTree(), ".gitignore"); FileUtils.createNewFile(ignoreFile); - writer = new PrintWriter(ignoreFile); - writer.print("sub/b.txt"); - writer.close(); + try (PrintWriter writer = new PrintWriter(ignoreFile)) { + writer.print("sub/b.txt"); + } File file2 = new File(db.getWorkTree(), "sub/b.txt"); FileUtils.createNewFile(file2); - writer = new PrintWriter(file2); - writer.print("content b"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file2)) { + writer.print("content b"); + } try (Git git = new Git(db)) { git.add().addFilepattern("sub").call(); @@ -823,15 +821,15 @@ public class AddCommandTest extends RepositoryTestCase { FileUtils.mkdir(new File(db.getWorkTree(), "sub")); File file = new File(db.getWorkTree(), "sub/a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } File file2 = new File(db.getWorkTree(), "sub/b.txt"); FileUtils.createNewFile(file2); - writer = new PrintWriter(file2); - writer.print("content b"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file2)) { + writer.print("content b"); + } try (Git git = new Git(db)) { git.add().addFilepattern(".").call(); @@ -851,15 +849,15 @@ public class AddCommandTest extends RepositoryTestCase { FileUtils.mkdir(new File(db.getWorkTree(), "sub")); File file = new File(db.getWorkTree(), "sub/a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } File file2 = new File(db.getWorkTree(), "sub/b.txt"); FileUtils.createNewFile(file2); - writer = new PrintWriter(file2); - writer.print("content b"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file2)) { + writer.print("content b"); + } try (Git git = new Git(db)) { git.add().addFilepattern("sub").call(); @@ -874,14 +872,14 @@ public class AddCommandTest extends RepositoryTestCase { // new unstaged file sub/c.txt File file3 = new File(db.getWorkTree(), "sub/c.txt"); FileUtils.createNewFile(file3); - writer = new PrintWriter(file3); - writer.print("content c"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file3)) { + writer.print("content c"); + } // file sub/a.txt is modified - writer = new PrintWriter(file); - writer.print("modified content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("modified content"); + } // file sub/b.txt is deleted FileUtils.delete(file2); @@ -906,15 +904,15 @@ public class AddCommandTest extends RepositoryTestCase { FileUtils.mkdir(new File(db.getWorkTree(), "sub")); File file = new File(db.getWorkTree(), "sub/a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } File file2 = new File(db.getWorkTree(), "sub/b.txt"); FileUtils.createNewFile(file2); - writer = new PrintWriter(file2); - writer.print("content b"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file2)) { + writer.print("content b"); + } try (Git git = new Git(db)) { git.add().addFilepattern("sub").call(); @@ -929,14 +927,14 @@ public class AddCommandTest extends RepositoryTestCase { // new unstaged file sub/c.txt File file3 = new File(db.getWorkTree(), "sub/c.txt"); FileUtils.createNewFile(file3); - writer = new PrintWriter(file3); - writer.print("content c"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file3)) { + writer.print("content c"); + } // file sub/a.txt is modified - writer = new PrintWriter(file); - writer.print("modified content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("modified content"); + } FileUtils.delete(file2); @@ -1250,9 +1248,9 @@ public class AddCommandTest extends RepositoryTestCase { try (Git git = new Git(db)) { git.add().addFilepattern("nested-repo").call(); - + // with gitlinks ignored, we treat this as a normal directory assertEquals( - "[nested-repo, mode:160000]", + "[nested-repo/README1.md, mode:100644][nested-repo/README2.md, mode:100644]", indexState(0)); } } @@ -1260,10 +1258,11 @@ public class AddCommandTest extends RepositoryTestCase { private static DirCacheEntry addEntryToBuilder(String path, File file, ObjectInserter newObjectInserter, DirCacheBuilder builder, int stage) throws IOException { - FileInputStream inputStream = new FileInputStream(file); - ObjectId id = newObjectInserter.insert( + ObjectId id; + try (FileInputStream inputStream = new FileInputStream(file)) { + id = newObjectInserter.insert( Constants.OBJ_BLOB, file.length(), inputStream); - inputStream.close(); + } DirCacheEntry entry = new DirCacheEntry(path, stage); entry.setObjectId(id); entry.setFileMode(FileMode.REGULAR_FILE); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java index 1201d9f391..71df59e1ff 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java @@ -74,8 +74,7 @@ import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; -import org.eclipse.jgit.lfs.CleanFilter; -import org.eclipse.jgit.lfs.SmudgeFilter; +import org.eclipse.jgit.lfs.BuiltinLFS; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; @@ -102,8 +101,7 @@ public class CheckoutCommandTest extends RepositoryTestCase { @Override @Before public void setUp() throws Exception { - CleanFilter.register(); - SmudgeFilter.register(); + BuiltinLFS.register(); super.setUp(); git = new Git(db); // commit something @@ -172,15 +170,12 @@ public class CheckoutCommandTest extends RepositoryTestCase { @Test public void testCheckoutWithNonDeletedFiles() throws Exception { File testFile = writeTrashFile("temp", ""); - FileInputStream fis = new FileInputStream(testFile); - try { + try (FileInputStream fis = new FileInputStream(testFile)) { FileUtils.delete(testFile); return; } catch (IOException e) { // the test makes only sense if deletion of // a file with open stream fails - } finally { - fis.close(); } FileUtils.delete(testFile); CheckoutCommand co = git.checkout(); @@ -194,15 +189,12 @@ public class CheckoutCommandTest extends RepositoryTestCase { git.checkout().setName("master").call(); assertTrue(testFile.exists()); // lock the file so it can't be deleted (in Windows, that is) - fis = new FileInputStream(testFile); - try { + try (FileInputStream fis = new FileInputStream(testFile)) { assertEquals(Status.NOT_TRIED, co.getResult().getStatus()); co.setName("test").call(); assertTrue(testFile.exists()); assertEquals(Status.NONDELETED, co.getResult().getStatus()); assertTrue(co.getResult().getUndeletedList().contains("Test.txt")); - } finally { - fis.close(); } } @@ -824,7 +816,7 @@ public class CheckoutCommandTest extends RepositoryTestCase { } private File writeTempFile(String body) throws IOException { - File f = File.createTempFile("AddCommandTest_", ""); + File f = File.createTempFile("CheckoutCommandTest_", ""); JGitTestUtil.write(f, body); return f; } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java index 85436db472..065b5b4c3e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java @@ -48,10 +48,12 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.File; +import java.io.IOException; import java.util.Set; import java.util.TreeSet; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.NoFilepatternException; import org.eclipse.jgit.errors.NoWorkTreeException; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.Repository; @@ -238,8 +240,9 @@ public class CleanCommandTest extends RepositoryTestCase { command.setPath(path); String uri = db.getDirectory().toURI().toString(); command.setURI(uri); - Repository repo = command.call(); - repo.close(); + try (Repository repo = command.call()) { + // Unused + } Status beforeCleanStatus = git.status().call(); assertTrue(beforeCleanStatus.getAdded().contains(DOT_GIT_MODULES)); @@ -296,4 +299,18 @@ public class CleanCommandTest extends RepositoryTestCase { // The inner repository should be cleaned this time assertTrue(forceCleanedFiles.contains(innerRepoName + "/")); } + + @Test + // To proof Bug 514434. No assertions, but before the bugfix + // this test was throwing Exceptions + public void testFilesShouldBeCleanedInSubSubFolders() + throws IOException, NoFilepatternException, GitAPIException { + writeTrashFile(".gitignore", + "/ignored-dir\n/sub-noclean/Ignored.txt\n/this_is_ok\n/this_is/not_ok\n"); + git.add().addFilepattern(".gitignore").call(); + git.commit().setMessage("adding .gitignore").call(); + writeTrashFile("this_is_ok/more/subdirs/file.txt", "1"); + writeTrashFile("this_is/not_ok/more/subdirs/file.txt", "2"); + git.clean().setCleanDirectories(true).setIgnore(false).call(); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java index e687a6ca7f..0d7009dca4 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java @@ -601,17 +601,17 @@ public class CloneCommandTest extends RepositoryTestCase { SubmoduleWalk walk = SubmoduleWalk.forIndex(git2.getRepository()); assertTrue(walk.next()); - Repository clonedSub1 = walk.getRepository(); - assertNotNull(clonedSub1); - assertEquals( - new File(git2.getRepository().getWorkTree(), walk.getPath()), - clonedSub1.getWorkTree()); - assertEquals(new File(new File(git2.getRepository().getDirectory(), - "modules"), walk.getPath()), - clonedSub1.getDirectory()); - status = new SubmoduleStatusCommand(clonedSub1); - statuses = status.call(); - clonedSub1.close(); + try (Repository clonedSub1 = walk.getRepository()) { + assertNotNull(clonedSub1); + assertEquals(new File(git2.getRepository().getWorkTree(), + walk.getPath()), clonedSub1.getWorkTree()); + assertEquals( + new File(new File(git2.getRepository().getDirectory(), + "modules"), walk.getPath()), + clonedSub1.getDirectory()); + status = new SubmoduleStatusCommand(clonedSub1); + statuses = status.call(); + } pathStatus = statuses.get(path); assertNotNull(pathStatus); assertEquals(SubmoduleStatusType.INITIALIZED, pathStatus.getType()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java index 1d5c6742c7..ca0630ea35 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java @@ -120,9 +120,9 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { // create first file File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content1"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content1"); + } // First commit - a.txt file git.add().addFilepattern("a.txt").call(); @@ -131,9 +131,9 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { // create second file file = new File(db.getWorkTree(), "b.txt"); FileUtils.createNewFile(file); - writer = new PrintWriter(file); - writer.print("content2"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content2"); + } // Second commit - b.txt file git.add().addFilepattern("b.txt").call(); @@ -231,9 +231,9 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { JGitInternalException, GitAPIException { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content"); + } try (Git git = new Git(db)) { git.add().addFilepattern("a.txt").call(); @@ -242,9 +242,9 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea", tw.getObjectId(0).getName()); - writer = new PrintWriter(file); - writer.print("content2"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content2"); + } commit = git.commit().setMessage("second commit").call(); tw = TreeWalk.forPath(db, "a.txt", commit.getTree()); assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea", @@ -265,9 +265,9 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { // create file File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - PrintWriter writer = new PrintWriter(file); - writer.print("content1"); - writer.close(); + try (PrintWriter writer = new PrintWriter(file)) { + writer.print("content1"); + } // First commit - a.txt file git.add().addFilepattern("a.txt").call(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java index a0834e7e85..0dd3749337 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java @@ -223,9 +223,9 @@ public class CommitCommandTest extends RepositoryTestCase { assertEquals(uri, generator.getModulesUrl()); assertEquals(path, generator.getModulesPath()); assertEquals(uri, generator.getConfigUrl()); - Repository subModRepo = generator.getRepository(); - assertNotNull(subModRepo); - subModRepo.close(); + try (Repository subModRepo = generator.getRepository()) { + assertNotNull(subModRepo); + } assertEquals(commit, repo.resolve(Constants.HEAD)); RevCommit submoduleCommit = git.commit().setMessage("submodule add") @@ -273,9 +273,9 @@ public class CommitCommandTest extends RepositoryTestCase { assertEquals(uri, generator.getModulesUrl()); assertEquals(path, generator.getModulesPath()); assertEquals(uri, generator.getConfigUrl()); - Repository subModRepo = generator.getRepository(); - assertNotNull(subModRepo); - subModRepo.close(); + try (Repository subModRepo = generator.getRepository()) { + assertNotNull(subModRepo); + } assertEquals(commit2, repo.resolve(Constants.HEAD)); RevCommit submoduleAddCommit = git.commit().setMessage("submodule add") diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java index d78a328402..79da2da7ea 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java @@ -297,9 +297,9 @@ public class DescribeCommandTest extends RepositoryTestCase { } private static void touch(File f, String contents) throws Exception { - FileWriter w = new FileWriter(f); - w.write(contents); - w.close(); + try (FileWriter w = new FileWriter(f)) { + w.write(contents); + } } private String describe(ObjectId c1, boolean longDesc) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/HugeFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/HugeFileTest.java index 4208f4d8e3..6c3504aa48 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/HugeFileTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/HugeFileTest.java @@ -87,9 +87,9 @@ public class HugeFileTest extends RepositoryTestCase { public void testAddHugeFile() throws Exception { measure("Commencing test"); File file = new File(db.getWorkTree(), "a.txt"); - RandomAccessFile rf = new RandomAccessFile(file, "rw"); - rf.setLength(4429185024L); - rf.close(); + try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) { + rf.setLength(4429185024L); + } measure("Created file"); git.add().addFilepattern("a.txt").call(); @@ -109,9 +109,9 @@ public class HugeFileTest extends RepositoryTestCase { assertEquals(0, status.getUntracked().size()); // Does not change anything, but modified timestamp - rf = new RandomAccessFile(file, "rw"); - rf.write(0); - rf.close(); + try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) { + rf.write(0); + } status = git.status().call(); measure("Status after non-modifying update"); @@ -125,9 +125,9 @@ public class HugeFileTest extends RepositoryTestCase { assertEquals(0, status.getUntracked().size()); // Change something - rf = new RandomAccessFile(file, "rw"); - rf.write('a'); - rf.close(); + try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) { + rf.write('a'); + } status = git.status().call(); measure("Status after modifying update"); @@ -141,10 +141,10 @@ public class HugeFileTest extends RepositoryTestCase { assertEquals(0, status.getUntracked().size()); // Truncate mod 4G and re-establish equality - rf = new RandomAccessFile(file, "rw"); - rf.setLength(134217728L); - rf.write(0); - rf.close(); + try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) { + rf.setLength(134217728L); + rf.write(0); + } status = git.status().call(); measure("Status after truncating update"); @@ -158,9 +158,9 @@ public class HugeFileTest extends RepositoryTestCase { assertEquals(0, status.getUntracked().size()); // Change something - rf = new RandomAccessFile(file, "rw"); - rf.write('a'); - rf.close(); + try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) { + rf.write('a'); + } status = git.status().call(); measure("Status after modifying and truncating update"); @@ -174,10 +174,10 @@ public class HugeFileTest extends RepositoryTestCase { assertEquals(0, status.getUntracked().size()); // Truncate to entry length becomes negative int - rf = new RandomAccessFile(file, "rw"); - rf.setLength(3429185024L); - rf.write(0); - rf.close(); + try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) { + rf.setLength(3429185024L); + rf.write(0); + } git.add().addFilepattern("a.txt").call(); measure("Added truncated file"); @@ -197,9 +197,9 @@ public class HugeFileTest extends RepositoryTestCase { assertEquals(0, status.getUntracked().size()); // Change something - rf = new RandomAccessFile(file, "rw"); - rf.write('a'); - rf.close(); + try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) { + rf.write('a'); + } status = git.status().call(); measure("Status after modifying and truncating update"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java index e850223762..9e3ee2c566 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java @@ -69,14 +69,14 @@ public class InitCommandTest extends RepositoryTestCase { } @Test - public void testInitRepository() throws IOException, JGitInternalException, - GitAPIException { + public void testInitRepository() + throws IOException, JGitInternalException, GitAPIException { File directory = createTempDirectory("testInitRepository"); InitCommand command = new InitCommand(); command.setDirectory(directory); - Repository repository = command.call().getRepository(); - addRepoToClose(repository); - assertNotNull(repository); + try (Git git = command.call()) { + assertNotNull(git.getRepository()); + } } @Test @@ -89,9 +89,9 @@ public class InitCommandTest extends RepositoryTestCase { assertTrue(directory.listFiles().length > 0); InitCommand command = new InitCommand(); command.setDirectory(directory); - Repository repository = command.call().getRepository(); - addRepoToClose(repository); - assertNotNull(repository); + try (Git git = command.call()) { + assertNotNull(git.getRepository()); + } } @Test @@ -101,10 +101,11 @@ public class InitCommandTest extends RepositoryTestCase { InitCommand command = new InitCommand(); command.setDirectory(directory); command.setBare(true); - Repository repository = command.call().getRepository(); - addRepoToClose(repository); - assertNotNull(repository); - assertTrue(repository.isBare()); + try (Git git = command.call()) { + Repository repository = git.getRepository(); + assertNotNull(repository); + assertTrue(repository.isBare()); + } } // non-bare repos where gitDir and directory is set. Same as @@ -117,11 +118,12 @@ public class InitCommandTest extends RepositoryTestCase { InitCommand command = new InitCommand(); command.setDirectory(wt); command.setGitDir(gitDir); - Repository repository = command.call().getRepository(); - addRepoToClose(repository); - assertNotNull(repository); - assertEqualsFile(wt, repository.getWorkTree()); - assertEqualsFile(gitDir, repository.getDirectory()); + try (Git git = command.call()) { + Repository repository = git.getRepository(); + assertNotNull(repository); + assertEqualsFile(wt, repository.getWorkTree()); + assertEqualsFile(gitDir, repository.getDirectory()); + } } // non-bare repos where only gitDir is set. Same as @@ -135,12 +137,13 @@ public class InitCommandTest extends RepositoryTestCase { File gitDir = createTempDirectory("testInitRepository/.git"); InitCommand command = new InitCommand(); command.setGitDir(gitDir); - Repository repository = command.call().getRepository(); - addRepoToClose(repository); - assertNotNull(repository); - assertEqualsFile(gitDir, repository.getDirectory()); - assertEqualsFile(new File(reader.getProperty("user.dir")), - repository.getWorkTree()); + try (Git git = command.call()) { + Repository repository = git.getRepository(); + assertNotNull(repository); + assertEqualsFile(gitDir, repository.getDirectory()); + assertEqualsFile(new File(reader.getProperty("user.dir")), + repository.getWorkTree()); + } } // Bare repos where gitDir and directory is set will only work if gitDir and @@ -169,13 +172,14 @@ public class InitCommandTest extends RepositoryTestCase { .getAbsolutePath()); InitCommand command = new InitCommand(); command.setBare(false); - Repository repository = command.call().getRepository(); - addRepoToClose(repository); - assertNotNull(repository); - assertEqualsFile(new File(reader.getProperty("user.dir"), ".git"), - repository.getDirectory()); - assertEqualsFile(new File(reader.getProperty("user.dir")), - repository.getWorkTree()); + try (Git git = command.call()) { + Repository repository = git.getRepository(); + assertNotNull(repository); + assertEqualsFile(new File(reader.getProperty("user.dir"), ".git"), + repository.getDirectory()); + assertEqualsFile(new File(reader.getProperty("user.dir")), + repository.getWorkTree()); + } } // If neither directory nor gitDir is set in a bare repo make sure @@ -189,12 +193,13 @@ public class InitCommandTest extends RepositoryTestCase { .getAbsolutePath()); InitCommand command = new InitCommand(); command.setBare(true); - Repository repository = command.call().getRepository(); - addRepoToClose(repository); - assertNotNull(repository); - assertEqualsFile(new File(reader.getProperty("user.dir")), - repository.getDirectory()); - assertNull(repository.getWorkTree()); + try (Git git = command.call()) { + Repository repository = git.getRepository(); + assertNotNull(repository); + assertEqualsFile(new File(reader.getProperty("user.dir")), + repository.getDirectory()); + assertNull(repository.getWorkTree()); + } } // In a non-bare repo when directory and gitDir is set then they shouldn't diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NotesCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NotesCommandTest.java index 9d15699963..6e06e9545a 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NotesCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NotesCommandTest.java @@ -42,6 +42,7 @@ */ package org.eclipse.jgit.api; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import java.util.List; @@ -87,7 +88,7 @@ public class NotesCommandTest extends RepositoryTestCase { git.notesAdd().setObjectId(commit2).setMessage("data").call(); Note note = git.notesShow().setObjectId(commit2).call(); String content = new String(db.open(note.getData()).getCachedBytes(), - "UTF-8"); + UTF_8); assertEquals(content, "data"); git.notesRemove().setObjectId(commit2).call(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java index cc5a0249cb..9461c42500 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java @@ -591,34 +591,23 @@ public class PullCommandTest extends RepositoryTestCase { private static void writeToFile(File actFile, String string) throws IOException { - FileOutputStream fos = null; - try { - fos = new FileOutputStream(actFile); + try (FileOutputStream fos = new FileOutputStream(actFile)) { fos.write(string.getBytes(UTF_8)); - fos.close(); - } finally { - if (fos != null) - fos.close(); } } private static void assertFileContentsEqual(File actFile, String string) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); - FileInputStream fis = null; byte[] buffer = new byte[100]; - try { - fis = new FileInputStream(actFile); + try (FileInputStream fis = new FileInputStream(actFile)) { int read = fis.read(buffer); while (read > 0) { bos.write(buffer, 0, read); read = fis.read(buffer); } - String content = new String(bos.toByteArray(), "UTF-8"); + String content = new String(bos.toByteArray(), UTF_8); assertEquals(string, content); - } finally { - if (fis != null) - fis.close(); } } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java index dfe05b808e..913b4ac434 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java @@ -395,34 +395,23 @@ public class PullCommandWithRebaseTest extends RepositoryTestCase { private static void writeToFile(File actFile, String string) throws IOException { - FileOutputStream fos = null; - try { - fos = new FileOutputStream(actFile); + try (FileOutputStream fos = new FileOutputStream(actFile)) { fos.write(string.getBytes(UTF_8)); - fos.close(); - } finally { - if (fos != null) - fos.close(); } } private static void assertFileContentsEqual(File actFile, String string) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); - FileInputStream fis = null; byte[] buffer = new byte[100]; - try { - fis = new FileInputStream(actFile); + try (FileInputStream fis = new FileInputStream(actFile)) { int read = fis.read(buffer); while (read > 0) { bos.write(buffer, 0, read); read = fis.read(buffer); } - String content = new String(bos.toByteArray(), "UTF-8"); + String content = new String(bos.toByteArray(), UTF_8); assertEquals(string, content); - } finally { - if (fis != null) - fis.close(); } } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java index 3f43c6b1bb..2bf91aeed8 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java @@ -2103,9 +2103,8 @@ public class RebaseCommandTest extends RepositoryTestCase { private int countPicks() throws IOException { int count = 0; File todoFile = getTodoFile(); - BufferedReader br = new BufferedReader(new InputStreamReader( - new FileInputStream(todoFile), "UTF-8")); - try { + try (BufferedReader br = new BufferedReader(new InputStreamReader( + new FileInputStream(todoFile), UTF_8))) { String line = br.readLine(); while (line != null) { int firstBlank = line.indexOf(' '); @@ -2123,8 +2122,6 @@ public class RebaseCommandTest extends RepositoryTestCase { line = br.readLine(); } return count; - } finally { - br.close(); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/CGitAttributesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/CGitAttributesTest.java index 34838138e3..344d1af6a0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/CGitAttributesTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/CGitAttributesTest.java @@ -366,6 +366,34 @@ public class CGitAttributesTest extends RepositoryTestCase { } @Test + public void testDirectoryWildmatchDoesNotMatchFiles1() throws Exception { + createFiles("a", "dir/b", "dir/sub/c"); + writeTrashFile(".gitattributes", "**/ bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryWildmatchDoesNotMatchFiles2() throws Exception { + createFiles("a", "dir/b", "dir/sub/c"); + writeTrashFile(".gitattributes", "**/**/ bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryWildmatchDoesNotMatchFiles3() throws Exception { + createFiles("a", "x/b", "sub/x/c", "sub/x/d/e"); + writeTrashFile(".gitattributes", "x/**/ bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryWildmatchDoesNotMatchFiles4() throws Exception { + createFiles("a", "dir/x", "dir/sub1/x", "dir/sub2/x/y"); + writeTrashFile(".gitattributes", "x/**/ bar\n"); + assertSameAsCGit(); + } + + @Test public void testDirectoryMatchSubComplex() throws Exception { createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new"); writeTrashFile(".gitattributes", "s[rs]c/n*/ bar\n"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java index a4f3d18d1f..01ca88880f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java @@ -313,7 +313,6 @@ public class MergeGitAttributeTest extends RepositoryTestCase { WrongRepositoryStateException, NoMessageException, GitAPIException { RevCommit disableCheckedCommit; - FileInputStream mergeResultFile = null; // Set up a git with conflict commits on images try (Git git = new Git(db)) { // First commit @@ -352,15 +351,12 @@ public class MergeGitAttributeTest extends RepositoryTestCase { assertEquals(MergeStatus.CONFLICTING, mergeResult.getMergeStatus()); // Check that the image was not modified (no conflict marker added) - mergeResultFile = new FileInputStream( + try (FileInputStream mergeResultFile = new FileInputStream( db.getWorkTree().toPath().resolve(ENABLED_CHECKED_GIF) - .toFile()); - assertTrue(contentEquals( - getClass().getResourceAsStream(ENABLED_CHECKED_GIF), - mergeResultFile)); - } finally { - if (mergeResultFile != null) { - mergeResultFile.close(); + .toFile())) { + assertTrue(contentEquals( + getClass().getResourceAsStream(ENABLED_CHECKED_GIF), + mergeResultFile)); } } } @@ -373,7 +369,6 @@ public class MergeGitAttributeTest extends RepositoryTestCase { WrongRepositoryStateException, NoMessageException, GitAPIException { RevCommit disableCheckedCommit; - FileInputStream mergeResultFile = null; // Set up a git whith conflict commits on images try (Git git = new Git(db)) { // First commit @@ -412,14 +407,12 @@ public class MergeGitAttributeTest extends RepositoryTestCase { assertEquals(MergeStatus.CONFLICTING, mergeResult.getMergeStatus()); // Check that the image was not modified (not conflict marker added) - mergeResultFile = new FileInputStream(db.getWorkTree().toPath() - .resolve(ENABLED_CHECKED_GIF).toFile()); - assertTrue(contentEquals( - getClass().getResourceAsStream(ENABLED_CHECKED_GIF), - mergeResultFile)); - } finally { - if (mergeResultFile != null) { - mergeResultFile.close(); + try (FileInputStream mergeResultFile = new FileInputStream( + db.getWorkTree().toPath().resolve(ENABLED_CHECKED_GIF) + .toFile())) { + assertTrue(contentEquals( + getClass().getResourceAsStream(ENABLED_CHECKED_GIF), + mergeResultFile)); } } } @@ -432,7 +425,6 @@ public class MergeGitAttributeTest extends RepositoryTestCase { NoMessageException, GitAPIException { RevCommit disableCheckedCommit; - FileInputStream mergeResultFile = null; // Set up a git whith conflict commits on images try (Git git = new Git(db)) { // First commit @@ -471,14 +463,12 @@ public class MergeGitAttributeTest extends RepositoryTestCase { assertEquals(MergeStatus.CONFLICTING, mergeResult.getMergeStatus()); // Check that the image was not modified (not conflict marker added) - mergeResultFile = new FileInputStream(db.getWorkTree().toPath() - .resolve(ENABLED_CHECKED_GIF).toFile()); - assertFalse(contentEquals( - getClass().getResourceAsStream(ENABLED_CHECKED_GIF), - mergeResultFile)); - } finally { - if (mergeResultFile != null) { - mergeResultFile.close(); + try (FileInputStream mergeResultFile = new FileInputStream( + db.getWorkTree().toPath().resolve(ENABLED_CHECKED_GIF) + .toFile())) { + assertFalse(contentEquals( + getClass().getResourceAsStream(ENABLED_CHECKED_GIF), + mergeResultFile)); } } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterReflowTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterReflowTest.java index 31f70cf28c..49e5d1b3d4 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterReflowTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterReflowTest.java @@ -181,17 +181,14 @@ public class DiffFormatterReflowTest { } private Patch parseTestPatchFile(final String patchFile) throws IOException { - final InputStream in = getClass().getResourceAsStream(patchFile); - if (in == null) { - fail("No " + patchFile + " test vector"); - return null; // Never happens - } - try { + try (InputStream in = getClass().getResourceAsStream(patchFile)) { + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } final Patch p = new Patch(); p.parse(in); return p; - } finally { - in.close(); } } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java index fabf03440a..45832f4958 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java @@ -55,12 +55,14 @@ import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.patch.FileHeader; import org.eclipse.jgit.patch.HunkHeader; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.util.FileUtils; @@ -485,6 +487,102 @@ public class DiffFormatterTest extends RepositoryTestCase { } } + @Test + public void testDiffAutoCrlfSmallFile() throws Exception { + String content = "01234\r\n01234\r\n01234\r\n"; + String expectedDiff = "diff --git a/test.txt b/test.txt\n" + + "index fe25983..a44a032 100644\n" // + + "--- a/test.txt\n" // + + "+++ b/test.txt\n" // + + "@@ -1,3 +1,4 @@\n" // + + " 01234\n" // + + "+ABCD\n" // + + " 01234\n" // + + " 01234\n"; + doAutoCrLfTest(content, expectedDiff); + } + + @Test + public void testDiffAutoCrlfMediumFile() throws Exception { + String content = mediumCrLfString(); + String expectedDiff = "diff --git a/test.txt b/test.txt\n" + + "index 215c502..c10f08c 100644\n" // + + "--- a/test.txt\n" // + + "+++ b/test.txt\n" // + + "@@ -1,4 +1,5 @@\n" // + + " 01234567\n" // + + "+ABCD\n" // + + " 01234567\n" // + + " 01234567\n" // + + " 01234567\n"; + doAutoCrLfTest(content, expectedDiff); + } + + @Test + public void testDiffAutoCrlfLargeFile() throws Exception { + String content = largeCrLfString(); + String expectedDiff = "diff --git a/test.txt b/test.txt\n" + + "index 7014942..c0487a7 100644\n" // + + "--- a/test.txt\n" // + + "+++ b/test.txt\n" // + + "@@ -1,4 +1,5 @@\n" + + " 012345678901234567890123456789012345678901234567\n" + + "+ABCD\n" + + " 012345678901234567890123456789012345678901234567\n" + + " 012345678901234567890123456789012345678901234567\n" + + " 012345678901234567890123456789012345678901234567\n"; + doAutoCrLfTest(content, expectedDiff); + } + + private void doAutoCrLfTest(String content, String expectedDiff) + throws Exception { + FileBasedConfig config = db.getConfig(); + config.setString(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_AUTOCRLF, "true"); + config.save(); + commitFile("test.txt", content, "master"); + // Insert a line into content + int i = content.indexOf('\n'); + content = content.substring(0, i + 1) + "ABCD\r\n" + + content.substring(i + 1); + writeTrashFile("test.txt", content); + // Create the patch + try (ByteArrayOutputStream os = new ByteArrayOutputStream(); + DiffFormatter dfmt = new DiffFormatter( + new BufferedOutputStream(os))) { + dfmt.setRepository(db); + dfmt.format(new DirCacheIterator(db.readDirCache()), + new FileTreeIterator(db)); + dfmt.flush(); + + String actual = os.toString("UTF-8"); + + assertEquals(expectedDiff, actual); + } + } + + private static String largeCrLfString() { + String line = "012345678901234567890123456789012345678901234567\r\n"; + StringBuilder builder = new StringBuilder( + 2 * RawText.FIRST_FEW_BYTES); + while (builder.length() < 2 * RawText.FIRST_FEW_BYTES) { + builder.append(line); + } + return builder.toString(); + } + + private static String mediumCrLfString() { + // Create a CR-LF string longer than RawText.FIRST_FEW_BYTES whose + // canonical representation is shorter than RawText.FIRST_FEW_BYTES. + String line = "01234567\r\n"; // 10 characters + StringBuilder builder = new StringBuilder( + RawText.FIRST_FEW_BYTES + line.length()); + while (builder.length() <= RawText.FIRST_FEW_BYTES) { + builder.append(line); + } + return builder.toString(); + } + private static String makeDiffHeader(String pathA, String pathB, ObjectId aId, ObjectId bId) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java index 92ce4e178b..dec17623fc 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.dircache; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.junit.Assert.assertEquals; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -234,32 +235,26 @@ public class DirCacheCGitCompatabilityTest extends LocalDiskRepositoryTestCase { private static Map<String, CGitIndexRecord> readLsFiles() throws Exception { final LinkedHashMap<String, CGitIndexRecord> r = new LinkedHashMap<>(); - final BufferedReader br = new BufferedReader(new InputStreamReader( - new FileInputStream(pathOf("gitgit.lsfiles")), "UTF-8")); - try { + try (BufferedReader br = new BufferedReader(new InputStreamReader( + new FileInputStream(pathOf("gitgit.lsfiles")), UTF_8))) { String line; while ((line = br.readLine()) != null) { final CGitIndexRecord cr = new CGitIndexRecord(line); r.put(cr.path, cr); } - } finally { - br.close(); } return r; } private static Map<String, CGitLsTreeRecord> readLsTree() throws Exception { final LinkedHashMap<String, CGitLsTreeRecord> r = new LinkedHashMap<>(); - final BufferedReader br = new BufferedReader(new InputStreamReader( - new FileInputStream(pathOf("gitgit.lstree")), "UTF-8")); - try { + try (BufferedReader br = new BufferedReader(new InputStreamReader( + new FileInputStream(pathOf("gitgit.lstree")), UTF_8))) { String line; while ((line = br.readLine()) != null) { final CGitLsTreeRecord cr = new CGitLsTreeRecord(line); r.put(cr.path, cr); } - } finally { - br.close(); } return r; } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java index 9afb58ecfb..2253a0421f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java @@ -186,6 +186,59 @@ public class RepoCommandTest extends RepositoryTestCase { } @Test + public void runTwiceIsNOP() throws Exception { + Repository child = Git.cloneRepository() + .setURI(groupADb.getDirectory().toURI().toString()) + .setDirectory(createUniqueTestGitDir(true)).setBare(true).call() + .getRepository(); + + Repository dest = Git.cloneRepository() + .setURI(db.getDirectory().toURI().toString()) + .setDirectory(createUniqueTestGitDir(true)).setBare(true).call() + .getRepository(); + + assertTrue(dest.isBare()); + assertTrue(child.isBare()); + + StringBuilder xmlContent = new StringBuilder(); + xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") + .append("<manifest>") + .append("<remote name=\"remote1\" fetch=\"..\" />") + .append("<default revision=\"master\" remote=\"remote1\" />") + .append("<project path=\"base\" name=\"platform/base\" />") + .append("</manifest>"); + RepoCommand cmd = new RepoCommand(dest); + + IndexedRepos repos = new IndexedRepos(); + repos.put("platform/base", child); + + RevCommit commit = cmd + .setInputStream(new ByteArrayInputStream( + xmlContent.toString().getBytes(UTF_8))) + .setRemoteReader(repos) + .setURI("platform/") + .setTargetURI("platform/superproject") + .setRecordRemoteBranch(true) + .setRecordSubmoduleLabels(true) + .call(); + + String firstIdStr = commit.getId().name() + ":" + ".gitmodules"; + commit = new RepoCommand(dest) + .setInputStream(new ByteArrayInputStream( + xmlContent.toString().getBytes(UTF_8))) + .setRemoteReader(repos) + .setURI("platform/") + .setTargetURI("platform/superproject") + .setRecordRemoteBranch(true) + .setRecordSubmoduleLabels(true) + .call(); + String idStr = commit.getId().name() + ":" + ".gitmodules"; + assertEquals(firstIdStr, idStr); + child.close(); + dest.close(); + } + + @Test public void androidSetup() throws Exception { Repository child = Git.cloneRepository() .setURI(groupADb.getDirectory().toURI().toString()) @@ -213,8 +266,7 @@ public class RepoCommandTest extends RepositoryTestCase { repos.put("platform/base", child); RevCommit commit = cmd - .setInputStream(new ByteArrayInputStream( - xmlContent.toString().getBytes(UTF_8))) + .setInputStream(new ByteArrayInputStream(xmlContent.toString().getBytes(UTF_8))) .setRemoteReader(repos) .setURI("platform/") .setTargetURI("platform/superproject") @@ -238,6 +290,48 @@ public class RepoCommandTest extends RepositoryTestCase { } @Test + public void recordUnreachableRemotes() throws Exception { + StringBuilder xmlContent = new StringBuilder(); + xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") + .append("<manifest>") + .append("<remote name=\"remote1\" fetch=\"https://host.com/\" />") + .append("<default revision=\"master\" remote=\"remote1\" />") + .append("<project path=\"base\" name=\"platform/base\" />") + .append("</manifest>"); + + Repository dest = Git.cloneRepository() + .setURI(db.getDirectory().toURI().toString()) + .setDirectory(createUniqueTestGitDir(true)).setBare(true).call() + .getRepository(); + + assertTrue(dest.isBare()); + + RevCommit commit = new RepoCommand(dest) + .setInputStream(new ByteArrayInputStream( + xmlContent.toString().getBytes(UTF_8))) + .setRemoteReader(new IndexedRepos()) + .setURI("platform/") + .setTargetURI("platform/superproject") + .setRecordRemoteBranch(true) + .setIgnoreRemoteFailures(true) + .setRecordSubmoduleLabels(true) + .call(); + + String idStr = commit.getId().name() + ":" + ".gitmodules"; + ObjectId modId = dest.resolve(idStr); + + try (ObjectReader reader = dest.newObjectReader()) { + byte[] bytes = reader.open(modId).getCachedBytes(Integer.MAX_VALUE); + Config base = new Config(); + BlobBasedConfig cfg = new BlobBasedConfig(base, bytes); + String subUrl = cfg.getString("submodule", "base", "url"); + assertEquals(subUrl, "https://host.com/platform/base"); + } + + dest.close(); + } + + @Test public void gerritSetup() throws Exception { Repository child = Git.cloneRepository().setURI(groupADb.getDirectory().toURI().toString()) @@ -345,6 +439,63 @@ public class RepoCommandTest extends RepositoryTestCase { } @Test + public void absoluteRemoteURLAbsoluteTargetURL() throws Exception { + Repository child = + Git.cloneRepository().setURI(groupADb.getDirectory().toURI().toString()) + .setDirectory(createUniqueTestGitDir(true)) + .setBare(true).call().getRepository(); + Repository dest = Git.cloneRepository() + .setURI(db.getDirectory().toURI().toString()).setDirectory(createUniqueTestGitDir(true)) + .setBare(true).call().getRepository(); + String abs = "https://chromium.googlesource.com"; + String repoUrl = "https://chromium.googlesource.com/chromium/src"; + boolean fetchSlash = false; + boolean baseSlash = false; + do { + do { + String fetchUrl = fetchSlash ? abs + "/" : abs; + String baseUrl = baseSlash ? abs + "/" : abs; + + StringBuilder xmlContent = new StringBuilder(); + xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") + .append("<manifest>") + .append("<remote name=\"origin\" fetch=\"" + fetchUrl + "\" />") + .append("<default revision=\"master\" remote=\"origin\" />") + .append("<project path=\"src\" name=\"chromium/src\" />") + .append("</manifest>"); + RepoCommand cmd = new RepoCommand(dest); + + IndexedRepos repos = new IndexedRepos(); + repos.put(repoUrl, child); + + RevCommit commit = cmd + .setInputStream(new ByteArrayInputStream(xmlContent.toString().getBytes(UTF_8))) + .setRemoteReader(repos) + .setURI(baseUrl) + .setTargetURI(abs + "/superproject") + .setRecordRemoteBranch(true) + .setRecordSubmoduleLabels(true) + .call(); + + String idStr = commit.getId().name() + ":" + ".gitmodules"; + ObjectId modId = dest.resolve(idStr); + + try (ObjectReader reader = dest.newObjectReader()) { + byte[] bytes = reader.open(modId).getCachedBytes(Integer.MAX_VALUE); + Config base = new Config(); + BlobBasedConfig cfg = new BlobBasedConfig(base, bytes); + String subUrl = cfg.getString("submodule", "src", "url"); + assertEquals("../chromium/src", subUrl); + } + fetchSlash = !fetchSlash; + } while (fetchSlash); + baseSlash = !baseSlash; + } while (baseSlash); + child.close(); + dest.close(); + } + + @Test public void testAddRepoManifest() throws Exception { StringBuilder xmlContent = new StringBuilder(); xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") @@ -814,37 +965,6 @@ public class RepoCommandTest extends RepositoryTestCase { assertEquals("submodule content should be as expected", "master world", content); } - - @Test - public void testNonDefaultRemotes() throws Exception { - StringBuilder xmlContent = new StringBuilder(); - xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") - .append("<manifest>") - .append("<remote name=\"remote1\" fetch=\".\" />") - .append("<remote name=\"remote2\" fetch=\"") - .append(notDefaultUri) - .append("\" />") - .append("<default revision=\"master\" remote=\"remote1\" />") - .append("<project path=\"foo\" name=\"") - .append(defaultUri) - .append("\" />") - .append("<project path=\"bar\" name=\".\" remote=\"remote2\" />") - .append("</manifest>"); - - Repository localDb = createWorkRepository(); - JGitTestUtil.writeTrashFile( - localDb, "manifest.xml", xmlContent.toString()); - RepoCommand command = new RepoCommand(localDb); - command - .setPath(localDb.getWorkTree().getAbsolutePath() + "/manifest.xml") - .setURI(rootUri) - .call(); - File file = new File(localDb.getWorkTree(), "foo/hello.txt"); - assertTrue("We should have foo", file.exists()); - file = new File(localDb.getWorkTree(), "bar/world.txt"); - assertTrue("We should have bar", file.exists()); - } - @Test public void testRemoteAlias() throws Exception { StringBuilder xmlContent = new StringBuilder(); @@ -1125,5 +1245,6 @@ public class RepoCommandTest extends RepositoryTestCase { testRelative("abc", "/bcd", "/bcd"); testRelative("http://a", "a/b", "a/b"); testRelative("http://base.com/a/", "http://child.com/a/b", "http://child.com/a/b"); + testRelative("http://base.com/a/", "http://base.com/a/b", "b"); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java index ee8191ffc5..3b11616fe6 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java @@ -118,20 +118,41 @@ public class CGitIgnoreTest extends RepositoryTestCase { } } - private LinkedHashSet<String> jgitIgnored() throws IOException { + private String[] cgitUntracked() throws Exception { + FS fs = db.getFS(); + ProcessBuilder builder = fs.runInShell("git", + new String[] { "ls-files", "--exclude-standard", "-o" }); + builder.directory(db.getWorkTree()); + builder.environment().put("HOME", fs.userHome().getAbsolutePath()); + ExecutionResult result = fs.execute(builder, + new ByteArrayInputStream(new byte[0])); + String errorOut = toString(result.getStderr()); + assertEquals("External git failed", "exit 0\n", + "exit " + result.getRc() + '\n' + errorOut); + try (BufferedReader r = new BufferedReader(new InputStreamReader( + new BufferedInputStream(result.getStdout().openInputStream()), + Constants.CHARSET))) { + return r.lines().toArray(String[]::new); + } + } + + private void jgitIgnoredAndUntracked(LinkedHashSet<String> ignored, + LinkedHashSet<String> untracked) throws IOException { // Do a tree walk that does descend into ignored directories and return // a list of all ignored files - LinkedHashSet<String> result = new LinkedHashSet<>(); try (TreeWalk walk = new TreeWalk(db)) { walk.addTree(new FileTreeIterator(db)); walk.setRecursive(true); while (walk.next()) { if (walk.getTree(WorkingTreeIterator.class).isEntryIgnored()) { - result.add(walk.getPathString()); + ignored.add(walk.getPathString()); + } else { + // tests of this class won't add any files to the index, + // hence everything what is not ignored is untracked + untracked.add(walk.getPathString()); } } } - return result; } private void assertNoIgnoredVisited(Set<String> ignored) throws Exception { @@ -150,9 +171,13 @@ public class CGitIgnoreTest extends RepositoryTestCase { } private void assertSameAsCGit(String... notIgnored) throws Exception { - LinkedHashSet<String> ignored = jgitIgnored(); + LinkedHashSet<String> ignored = new LinkedHashSet<>(); + LinkedHashSet<String> untracked = new LinkedHashSet<>(); + jgitIgnoredAndUntracked(ignored, untracked); String[] cgit = cgitIgnored(); + String[] cgitUntracked = cgitUntracked(); assertArrayEquals(cgit, ignored.toArray()); + assertArrayEquals(cgitUntracked, untracked.toArray()); for (String notExcluded : notIgnored) { assertFalse("File " + notExcluded + " should not be ignored", ignored.contains(notExcluded)); @@ -242,6 +267,34 @@ public class CGitIgnoreTest extends RepositoryTestCase { } @Test + public void testDirectoryWildmatchDoesNotMatchFiles1() throws Exception { + createFiles("a", "dir/b", "dir/sub/c"); + writeTrashFile(".gitignore", "**/\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryWildmatchDoesNotMatchFiles2() throws Exception { + createFiles("a", "dir/b", "dir/sub/c"); + writeTrashFile(".gitignore", "**/**/\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryWildmatchDoesNotMatchFiles3() throws Exception { + createFiles("a", "x/b", "sub/x/c", "sub/x/d/e"); + writeTrashFile(".gitignore", "x/**/\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryWildmatchDoesNotMatchFiles4() throws Exception { + createFiles("a", "dir/x", "dir/sub1/x", "dir/sub2/x/y"); + writeTrashFile(".gitignore", "**/x/\n"); + assertSameAsCGit(); + } + + @Test public void testUnescapedBracketsInGroup() throws Exception { createFiles("[", "]", "[]", "][", "[[]", "[]]", "[[]]"); writeTrashFile(".gitignore", "[[]]\n"); @@ -268,4 +321,68 @@ public class CGitIgnoreTest extends RepositoryTestCase { writeTrashFile(".gitignore", "[\\[\\]]\n"); assertSameAsCGit(); } + + @Test + public void testSimpleRootGitIgnoreGlobalNegation1() throws Exception { + // see IgnoreNodeTest.testSimpleRootGitIgnoreGlobalNegation1 + createFiles("x1", "a/x2", "x3/y"); + writeTrashFile(".gitignore", "*\n!x*"); + assertSameAsCGit(); + } + + @Test + public void testRepeatedNegationInDifferentFiles5() throws Exception { + // see IgnoreNodeTest.testRepeatedNegationInDifferentFiles5 + createFiles("a/b/e/nothere.o"); + writeTrashFile(".gitignore", "e"); + writeTrashFile("a/.gitignore", "e"); + writeTrashFile("a/b/.gitignore", "!e"); + assertSameAsCGit(); + } + + @Test + public void testRepeatedNegationInDifferentFilesWithWildmatcher1() + throws Exception { + createFiles("e", "x/e/f", "a/e/x1", "a/e/x2", "a/e/y", "a/e/sub/y"); + writeTrashFile(".gitignore", "a/e/**"); + writeTrashFile("a/.gitignore", "!e/x*"); + assertSameAsCGit(); + } + + @Test + public void testRepeatedNegationInDifferentFilesWithWildmatcher2() + throws Exception { + createFiles("e", "dir/f", "dir/g/h", "a/dir/i", "a/dir/j/k", + "a/b/dir/l", "a/b/dir/m/n", "a/b/dir/m/o/p", "a/q/dir/r", + "a/q/dir/dir/s", "c/d/dir/x", "c/d/dir/y"); + writeTrashFile(".gitignore", "**/dir/*"); + writeTrashFile("a/.gitignore", "!dir/*"); + writeTrashFile("a/b/.gitignore", "!**/dir/*"); + writeTrashFile("c/.gitignore", "!d/dir/x"); + assertSameAsCGit(); + } + + @Test + public void testNegationForSubDirectoryWithinIgnoredDirectoryHasNoEffect1() + throws Exception { + createFiles("e", "a/f", "a/b/g", "a/b/h/i"); + writeTrashFile(".gitignore", "a/b"); + writeTrashFile("a/.gitignore", "!b/*"); + assertSameAsCGit(); + } + + /* + * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=407475 + */ + @Test + public void testNegationAllExceptJavaInSrcAndExceptChildDirInSrc() + throws Exception { + // see + // IgnoreNodeTest.testNegationAllExceptJavaInSrcAndExceptChildDirInSrc + createFiles("nothere.o", "src/keep.java", "src/nothere.o", + "src/a/keep.java", "src/a/keep.o"); + writeTrashFile(".gitignore", "/*\n!/src/"); + writeTrashFile("src/.gitignore", "*\n!*.java\n!*/"); + assertSameAsCGit(); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java index 06164c8a91..2a1721e66c 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java @@ -48,10 +48,18 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import org.junit.Before; import org.junit.Test; public class FastIgnoreRuleTest { + private boolean pathMatch; + + @Before + public void setup() { + pathMatch = false; + } + @Test public void testSimpleCharClass() { assertMatched("][a]", "]a"); @@ -410,6 +418,19 @@ public class FastIgnoreRuleTest { assertMatched("a/**/b/**/c", "a/c/b/d/c"); assertMatched("a/**/**/b/**/**/c", "a/c/b/d/c"); + + assertMatched("**/", "a/"); + assertMatched("**/", "a/b"); + assertMatched("**/", "a/b/c"); + assertMatched("**/**/", "a/"); + assertMatched("**/**/", "a/b"); + assertMatched("**/**/", "a/b/"); + assertMatched("**/**/", "a/b/c"); + assertMatched("x/**/", "x/a/"); + assertMatched("x/**/", "x/a/b"); + assertMatched("x/**/", "x/a/b/"); + assertMatched("**/x/", "a/x/"); + assertMatched("**/x/", "a/b/x/"); } @Test @@ -424,6 +445,10 @@ public class FastIgnoreRuleTest { assertNotMatched("!/**/*.zip", "c/a/b.zip"); assertNotMatched("!**/*.zip", "c/a/b.zip"); assertNotMatched("a/**/b", "a/c/bb"); + + assertNotMatched("**/", "a"); + assertNotMatched("**/**/", "a"); + assertNotMatched("**/x/", "a/b/x"); } @SuppressWarnings("unused") @@ -465,6 +490,28 @@ public class FastIgnoreRuleTest { split("/a/b/c/", '/').toArray()); } + @Test + public void testPathMatch() { + pathMatch = true; + assertMatched("a", "a"); + assertMatched("a/", "a/"); + assertNotMatched("a/", "a/b"); + + assertMatched("**", "a"); + assertMatched("**", "a/"); + assertMatched("**", "a/b"); + + assertNotMatched("**/", "a"); + assertNotMatched("**/", "a/b"); + assertMatched("**/", "a/"); + assertMatched("**/", "a/b/"); + + assertNotMatched("x/**/", "x/a"); + assertNotMatched("x/**/", "x/a/b"); + assertMatched("x/**/", "x/a/"); + assertMatched("x/**/", "x/y/a/"); + } + private void assertMatched(String pattern, String path) { boolean match = match(pattern, path); String result = path + " is " + (match ? "ignored" : "not ignored") @@ -520,7 +567,7 @@ public class FastIgnoreRuleTest { FastIgnoreRule r = new FastIgnoreRule(pattern); // If speed of this test is ever an issue, we can use a presetRule field // to avoid recompiling a pattern each time. - boolean match = r.isMatch(target, isDirectory); + boolean match = r.isMatch(target, isDirectory, pathMatch); if (r.getNegation()) match = !match; return match; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java index 1178eb3e8a..ccc64fb468 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java @@ -81,6 +81,141 @@ public class IgnoreNodeTest extends RepositoryTestCase { private TreeWalk walk; @Test + public void testSimpleRootGitIgnoreGlobalIgnore() throws IOException { + writeIgnoreFile(".gitignore", "x"); + + writeTrashFile("a/x/file", ""); + writeTrashFile("b/x", ""); + writeTrashFile("x/file", ""); + + beginWalk(); + assertEntry(F, tracked, ".gitignore"); + assertEntry(D, tracked, "a"); + assertEntry(D, ignored, "a/x"); + assertEntry(F, ignored, "a/x/file"); + assertEntry(D, tracked, "b"); + assertEntry(F, ignored, "b/x"); + assertEntry(D, ignored, "x"); + assertEntry(F, ignored, "x/file"); + endWalk(); + } + + @Test + public void testSimpleRootGitIgnoreGlobalDirIgnore() throws IOException { + writeIgnoreFile(".gitignore", "x/"); + + writeTrashFile("a/x/file", ""); + writeTrashFile("x/file", ""); + + beginWalk(); + assertEntry(F, tracked, ".gitignore"); + assertEntry(D, tracked, "a"); + assertEntry(D, ignored, "a/x"); + assertEntry(F, ignored, "a/x/file"); + assertEntry(D, ignored, "x"); + assertEntry(F, ignored, "x/file"); + endWalk(); + } + + @Test + public void testSimpleRootGitIgnoreWildMatcher() throws IOException { + writeIgnoreFile(".gitignore", "**"); + + writeTrashFile("a/x", ""); + writeTrashFile("y", ""); + + beginWalk(); + assertEntry(F, ignored, ".gitignore"); + assertEntry(D, ignored, "a"); + assertEntry(F, ignored, "a/x"); + assertEntry(F, ignored, "y"); + endWalk(); + } + + @Test + public void testSimpleRootGitIgnoreWildMatcherDirOnly() throws IOException { + writeIgnoreFile(".gitignore", "**/"); + + writeTrashFile("a/x", ""); + writeTrashFile("y", ""); + + beginWalk(); + assertEntry(F, tracked, ".gitignore"); + assertEntry(D, ignored, "a"); + assertEntry(F, ignored, "a/x"); + assertEntry(F, tracked, "y"); + endWalk(); + } + + @Test + public void testSimpleRootGitIgnoreGlobalNegation1() throws IOException { + writeIgnoreFile(".gitignore", "*", "!x*"); + writeTrashFile("x1", ""); + writeTrashFile("a/x2", ""); + writeTrashFile("x3/y", ""); + + beginWalk(); + assertEntry(F, ignored, ".gitignore"); + assertEntry(D, ignored, "a"); + assertEntry(F, ignored, "a/x2"); + assertEntry(F, tracked, "x1"); + assertEntry(D, tracked, "x3"); + assertEntry(F, ignored, "x3/y"); + endWalk(); + } + + @Test + public void testSimpleRootGitIgnoreGlobalNegation2() throws IOException { + writeIgnoreFile(".gitignore", "*", "!x*", "!/a"); + writeTrashFile("x1", ""); + writeTrashFile("a/x2", ""); + writeTrashFile("x3/y", ""); + + beginWalk(); + assertEntry(F, ignored, ".gitignore"); + assertEntry(D, tracked, "a"); + assertEntry(F, tracked, "a/x2"); + assertEntry(F, tracked, "x1"); + assertEntry(D, tracked, "x3"); + assertEntry(F, ignored, "x3/y"); + endWalk(); + } + + @Test + public void testSimpleRootGitIgnoreGlobalNegation3() throws IOException { + writeIgnoreFile(".gitignore", "*", "!x*", "!x*/**"); + writeTrashFile("x1", ""); + writeTrashFile("a/x2", ""); + writeTrashFile("x3/y", ""); + + beginWalk(); + assertEntry(F, ignored, ".gitignore"); + assertEntry(D, ignored, "a"); + assertEntry(F, ignored, "a/x2"); + assertEntry(F, tracked, "x1"); + assertEntry(D, tracked, "x3"); + assertEntry(F, tracked, "x3/y"); + endWalk(); + } + + @Test + public void testSimpleRootGitIgnoreGlobalNegation4() throws IOException { + writeIgnoreFile(".gitignore", "*", "!**/"); + writeTrashFile("x1", ""); + writeTrashFile("a/x2", ""); + writeTrashFile("x3/y", ""); + + beginWalk(); + assertEntry(F, ignored, ".gitignore"); + assertEntry(D, tracked, "a"); + assertEntry(F, ignored, "a/x2"); + assertEntry(F, ignored, "x1"); + assertEntry(D, tracked, "x3"); + assertEntry(F, ignored, "x3/y"); + endWalk(); + } + + @Test public void testRules() throws IOException { writeIgnoreFile(".git/info/exclude", "*~", "/out"); @@ -210,7 +345,7 @@ public class IgnoreNodeTest extends RepositoryTestCase { assertEntry(F, ignored, "src/.gitignore"); assertEntry(D, tracked, "src/a"); assertEntry(F, tracked, "src/a/keep.java"); - assertEntry(F, tracked, "src/a/keep.o"); + assertEntry(F, ignored, "src/a/keep.o"); assertEntry(F, tracked, "src/keep.java"); assertEntry(F, ignored, "src/nothere.o"); endWalk(); @@ -316,6 +451,103 @@ public class IgnoreNodeTest extends RepositoryTestCase { } @Test + public void testRepeatedNegationInDifferentFiles5() throws IOException { + writeIgnoreFile(".gitignore", "e"); + writeIgnoreFile("a/.gitignore", "e"); + writeIgnoreFile("a/b/.gitignore", "!e"); + writeTrashFile("a/b/e/nothere.o", ""); + + beginWalk(); + assertEntry(F, tracked, ".gitignore"); + assertEntry(D, tracked, "a"); + assertEntry(F, tracked, "a/.gitignore"); + assertEntry(D, tracked, "a/b"); + assertEntry(F, tracked, "a/b/.gitignore"); + assertEntry(D, tracked, "a/b/e"); + assertEntry(F, tracked, "a/b/e/nothere.o"); + endWalk(); + } + + @Test + public void testIneffectiveNegationDifferentLevels1() throws IOException { + writeIgnoreFile(".gitignore", "a/b/e/", "!a/b/e/*"); + writeTrashFile("a/b/e/nothere.o", ""); + + beginWalk(); + assertEntry(F, tracked, ".gitignore"); + assertEntry(D, tracked, "a"); + assertEntry(D, tracked, "a/b"); + assertEntry(D, ignored, "a/b/e"); + assertEntry(F, ignored, "a/b/e/nothere.o"); + endWalk(); + } + + @Test + public void testIneffectiveNegationDifferentLevels2() throws IOException { + writeIgnoreFile(".gitignore", "a/b/e/"); + writeIgnoreFile("a/.gitignore", "!b/e/*"); + writeTrashFile("a/b/e/nothere.o", ""); + + beginWalk(); + assertEntry(F, tracked, ".gitignore"); + assertEntry(D, tracked, "a"); + assertEntry(F, tracked, "a/.gitignore"); + assertEntry(D, tracked, "a/b"); + assertEntry(D, ignored, "a/b/e"); + assertEntry(F, ignored, "a/b/e/nothere.o"); + endWalk(); + } + + @Test + public void testIneffectiveNegationDifferentLevels3() throws IOException { + writeIgnoreFile(".gitignore", "a/b/e/"); + writeIgnoreFile("a/b/.gitignore", "!e/*"); + writeTrashFile("a/b/e/nothere.o", ""); + + beginWalk(); + assertEntry(F, tracked, ".gitignore"); + assertEntry(D, tracked, "a"); + assertEntry(D, tracked, "a/b"); + assertEntry(F, tracked, "a/b/.gitignore"); + assertEntry(D, ignored, "a/b/e"); + assertEntry(F, ignored, "a/b/e/nothere.o"); + endWalk(); + } + + @Test + public void testIneffectiveNegationDifferentLevels4() throws IOException { + writeIgnoreFile(".gitignore", "a/b/e/"); + writeIgnoreFile("a/b/e/.gitignore", "!*"); + writeTrashFile("a/b/e/nothere.o", ""); + + beginWalk(); + assertEntry(F, tracked, ".gitignore"); + assertEntry(D, tracked, "a"); + assertEntry(D, tracked, "a/b"); + assertEntry(D, ignored, "a/b/e"); + assertEntry(F, ignored, "a/b/e/.gitignore"); + assertEntry(F, ignored, "a/b/e/nothere.o"); + endWalk(); + } + + @Test + public void testIneffectiveNegationDifferentLevels5() throws IOException { + writeIgnoreFile("a/.gitignore", "b/e/"); + writeIgnoreFile("a/b/.gitignore", "!e/*"); + writeTrashFile("a/b/e/nothere.o", ""); + + beginWalk(); + assertEntry(D, tracked, "a"); + assertEntry(F, tracked, "a/.gitignore"); + assertEntry(D, tracked, "a/b"); + assertEntry(F, tracked, "a/b/.gitignore"); + assertEntry(D, ignored, "a/b/e"); + assertEntry(F, ignored, "a/b/e/nothere.o"); + endWalk(); + } + + @SuppressWarnings("deprecation") + @Test public void testEmptyIgnoreNode() { // Rules are never empty: WorkingTreeIterator optimizes empty files away // So we have to test it manually in case third party clients use diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/indexdiff/IndexDiffWithSymlinkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/indexdiff/IndexDiffWithSymlinkTest.java index 3db27926c6..d5d3857ca4 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/indexdiff/IndexDiffWithSymlinkTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/indexdiff/IndexDiffWithSymlinkTest.java @@ -111,14 +111,12 @@ public class IndexDiffWithSymlinkTest extends LocalDiskRepositoryTestCase { && FS.DETECTED.supportsSymlinks()); super.setUp(); File testDir = createTempDirectory(this.getClass().getSimpleName()); - InputStream in = this.getClass().getClassLoader().getResourceAsStream( + try (InputStream in = this.getClass().getClassLoader() + .getResourceAsStream( this.getClass().getPackage().getName().replace('.', '/') + '/' - + FILEREPO + ".txt"); - assertNotNull("Test repo file not found", in); - try { + + FILEREPO + ".txt")) { + assertNotNull("Test repo file not found", in); testRepoDir = restoreGitRepo(in, testDir, FILEREPO); - } finally { - in.close(); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java index a9edf73b85..14863488ca 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java @@ -187,7 +187,10 @@ public class AbbreviationTest extends LocalDiskRepositoryTestCase { PackIndexWriter writer = new PackIndexWriterV2(dst); writer.write(objects, new byte[OBJECT_ID_LENGTH]); } - new FileOutputStream(packFile).close(); + + try (FileOutputStream unused = new FileOutputStream(packFile)) { + // unused + } assertEquals(id.abbreviate(20), reader.abbreviate(id, 2)); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java index 9998666052..9ceaa345d9 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java @@ -135,11 +135,8 @@ public class FileSnapshotTest { } private static void append(File f, byte b) throws IOException { - FileOutputStream os = new FileOutputStream(f, true); - try { + try (FileOutputStream os = new FileOutputStream(f, true)) { os.write(b); - } finally { - os.close(); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDeleteEmptyRefsFoldersTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDeleteEmptyRefsFoldersTest.java index b37cd7ae05..3caae72fc6 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDeleteEmptyRefsFoldersTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDeleteEmptyRefsFoldersTest.java @@ -129,4 +129,4 @@ public class GcDeleteEmptyRefsFoldersTest extends GcTestCase { assertTrue(ref01.toFile().exists()); assertTrue(ref02.toFile().exists()); } -} +}
\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java index b782ce87ff..8e438bc0e0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java @@ -95,6 +95,8 @@ import org.junit.Test; public class PackInserterTest extends RepositoryTestCase { private WindowCacheConfig origWindowCacheConfig; + private static final Random random = new Random(0); + @Before public void setWindowCacheConfig() { origWindowCacheConfig = new WindowCacheConfig(); @@ -518,7 +520,7 @@ public class PackInserterTest extends RepositoryTestCase { private static byte[] newLargeBlob() { byte[] blob = new byte[10240]; - new Random(0).nextBytes(blob); + random.nextBytes(blob); return blob; } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java index 63bd7f4684..dc05eeabe1 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java @@ -270,19 +270,16 @@ public class ReflogReaderTest extends SampleDataRepositoryTestCase { private void setupReflog(String logName, byte[] data) throws FileNotFoundException, IOException { - File logfile = new File(db.getDirectory(), logName); - if (!logfile.getParentFile().mkdirs() - && !logfile.getParentFile().isDirectory()) { - throw new IOException( - "oops, cannot create the directory for the test reflog file" - + logfile); - } - FileOutputStream fileOutputStream = new FileOutputStream(logfile); - try { - fileOutputStream.write(data); - } finally { - fileOutputStream.close(); - } - } + File logfile = new File(db.getDirectory(), logName); + if (!logfile.getParentFile().mkdirs() + && !logfile.getParentFile().isDirectory()) { + throw new IOException( + "oops, cannot create the directory for the test reflog file" + + logfile); + } + try (FileOutputStream fileOutputStream = new FileOutputStream(logfile)) { + fileOutputStream.write(data); + } + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java index 7f40bae556..1d188c3148 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java @@ -87,11 +87,8 @@ public class ReflogWriterTest extends SampleDataRepositoryTestCase { "oops, cannot create the directory for the test reflog file" + logfile); } - FileInputStream fileInputStream = new FileInputStream(logfile); - try { + try (FileInputStream fileInputStream = new FileInputStream(logfile)) { fileInputStream.read(buffer); - } finally { - fileInputStream.close(); } } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java index aa50697172..d7505af4cf 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java @@ -47,6 +47,7 @@ package org.eclipse.jgit.internal.storage.file; import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -521,7 +522,7 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase { 4294967295000L, 60)); commit.setCommitter(new PersonIdent("Joe Hacker", "joe2@example.com", 4294967295000L, 60)); - commit.setEncoding("UTF-8"); + commit.setEncoding(UTF_8); commit.setMessage("\u00dcbergeeks"); ObjectId cid = insertCommit(commit); assertEquals("4680908112778718f37e686cbebcc912730b3154", cid.name()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java index adba048e65..ec60bd9137 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java @@ -218,6 +218,27 @@ public class MergedReftableTest { } @Test + public void scanDuplicates() throws IOException { + List<Ref> delta1 = Arrays.asList( + ref("refs/heads/apple", 1), + ref("refs/heads/banana", 2)); + List<Ref> delta2 = Arrays.asList( + ref("refs/heads/apple", 3), + ref("refs/heads/apple", 4)); + + MergedReftable mr = merge(write(delta1, 1000), write(delta2, 2000)); + try (RefCursor rc = mr.allRefs()) { + assertTrue(rc.next()); + assertEquals("refs/heads/apple", rc.getRef().getName()); + assertEquals(id(3), rc.getRef().getObjectId()); + assertTrue(rc.next()); + assertEquals("refs/heads/banana", rc.getRef().getName()); + assertEquals(id(2), rc.getRef().getObjectId()); + assertFalse(rc.next()); + } + } + + @Test public void scanIncludeDeletes() throws IOException { List<Ref> delta1 = Arrays.asList(ref("refs/heads/next", 4)); List<Ref> delta2 = Arrays.asList(delete("refs/heads/next")); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java index fb1ee8cadb..7862005ebc 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java @@ -808,8 +808,14 @@ public class ConfigTest { fbConfig.load(); fail(); } catch (ConfigInvalidException cie) { - assertEquals(JGitText.get().tooManyIncludeRecursions, - cie.getCause().getMessage()); + for (Throwable t = cie; t != null; t = t.getCause()) { + if (t.getMessage() + .equals(JGitText.get().tooManyIncludeRecursions)) { + return; + } + } + fail("Expected to find expected exception message: " + + JGitText.get().tooManyIncludeRecursions); } } @@ -824,6 +830,80 @@ public class ConfigTest { assertFalse(parsed.getBoolean("foo", "bar", false)); } + @Test + public void testIncludeCaseInsensitiveSection() + throws IOException, ConfigInvalidException { + File included = tmp.newFile("included"); + String content = "[foo]\nbar=true\n"; + Files.write(included.toPath(), content.getBytes()); + + File config = tmp.newFile("config"); + content = "[Include]\npath=" + pathToString(included) + "\n"; + Files.write(config.toPath(), content.getBytes()); + + FileBasedConfig fbConfig = new FileBasedConfig(null, config, + FS.DETECTED); + fbConfig.load(); + assertTrue(fbConfig.getBoolean("foo", "bar", false)); + } + + @Test + public void testIncludeCaseInsensitiveKey() + throws IOException, ConfigInvalidException { + File included = tmp.newFile("included"); + String content = "[foo]\nbar=true\n"; + Files.write(included.toPath(), content.getBytes()); + + File config = tmp.newFile("config"); + content = "[include]\nPath=" + pathToString(included) + "\n"; + Files.write(config.toPath(), content.getBytes()); + + FileBasedConfig fbConfig = new FileBasedConfig(null, config, + FS.DETECTED); + fbConfig.load(); + assertTrue(fbConfig.getBoolean("foo", "bar", false)); + } + + @Test + public void testIncludeExceptionContainsLine() { + try { + parse("[include]\npath=\n"); + fail("Expected ConfigInvalidException"); + } catch (ConfigInvalidException e) { + assertTrue( + "Expected to find the problem line in the exception message", + e.getMessage().contains("include.path")); + } + } + + @Test + public void testIncludeExceptionContainsFile() throws IOException { + File included = tmp.newFile("included"); + String includedPath = pathToString(included); + String content = "[include]\npath=\n"; + Files.write(included.toPath(), content.getBytes()); + + File config = tmp.newFile("config"); + String include = "[include]\npath=" + includedPath + "\n"; + Files.write(config.toPath(), include.getBytes()); + FileBasedConfig fbConfig = new FileBasedConfig(null, config, + FS.DETECTED); + try { + fbConfig.load(); + fail("Expected ConfigInvalidException"); + } catch (ConfigInvalidException e) { + // Check that there is some exception in the chain that contains + // includedPath + for (Throwable t = e; t != null; t = t.getCause()) { + if (t.getMessage().contains(includedPath)) { + return; + } + } + fail("Expected to find the path in the exception message: " + + includedPath); + } + } + private static void assertReadLong(long exp) throws ConfigInvalidException { assertReadLong(exp, String.valueOf(exp)); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java index 4d42bd19c5..0412242ebf 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java @@ -1923,18 +1923,20 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { if (file.isFile()) { assertNotNull("found unexpected file for path " + path + " in workdir", expectedValue); - FileInputStream is = new FileInputStream(file); - byte[] buffer = new byte[(int) file.length()]; - int offset = 0; - int numRead = 0; - while (offset < buffer.length - && (numRead = is.read(buffer, offset, buffer.length - - offset)) >= 0) { - offset += numRead; + try (FileInputStream is = new FileInputStream(file)) { + byte[] buffer = new byte[(int) file.length()]; + int offset = 0; + int numRead = 0; + while (offset < buffer.length + && (numRead = is.read(buffer, offset, + buffer.length - offset)) >= 0) { + offset += numRead; + } + assertArrayEquals( + "unexpected content for path " + path + + " in workDir. ", + buffer, i.get(path).getBytes()); } - is.close(); - assertArrayEquals("unexpected content for path " + path - + " in workDir. ", buffer, i.get(path).getBytes()); nrFiles++; } else if (file.isDirectory()) { String[] files = file.list(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java index 4f26a27ef6..347883f842 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java @@ -68,13 +68,11 @@ public class MergeHeadMsgTest extends RepositoryTestCase { assertEquals(db.readMergeHeads().size(), 2); assertEquals(db.readMergeHeads().get(0), ObjectId.zeroId()); assertEquals(db.readMergeHeads().get(1), ObjectId.fromString(sampleId)); + // same test again, this time with lower-level io - FileOutputStream fos = new FileOutputStream(new File(db.getDirectory(), - "MERGE_HEAD")); - try { + try (FileOutputStream fos = new FileOutputStream( + new File(db.getDirectory(), "MERGE_HEAD"));) { fos.write("0000000000000000000000000000000000000000\n1c6db447abdbb291b25f07be38ea0b1bf94947c5\n".getBytes(Constants.CHARACTER_ENCODING)); - } finally { - fos.close(); } assertEquals(db.readMergeHeads().size(), 2); assertEquals(db.readMergeHeads().get(0), ObjectId.zeroId()); @@ -82,12 +80,9 @@ public class MergeHeadMsgTest extends RepositoryTestCase { db.writeMergeHeads(Collections.<ObjectId> emptyList()); assertEquals(read(new File(db.getDirectory(), "MERGE_HEAD")), ""); assertEquals(db.readMergeHeads(), null); - fos = new FileOutputStream(new File(db.getDirectory(), - "MERGE_HEAD")); - try { + try (FileOutputStream fos = new FileOutputStream( + new File(db.getDirectory(), "MERGE_HEAD"))) { fos.write(sampleId.getBytes(Constants.CHARACTER_ENCODING)); - } finally { - fos.close(); } assertEquals(db.readMergeHeads().size(), 1); assertEquals(db.readMergeHeads().get(0), ObjectId.fromString(sampleId)); @@ -103,12 +98,9 @@ public class MergeHeadMsgTest extends RepositoryTestCase { db.writeMergeCommitMsg(null); assertEquals(db.readMergeCommitMsg(), null); assertFalse(new File(db.getDirectory(), "MERGE_MSG").exists()); - FileOutputStream fos = new FileOutputStream(new File(db.getDirectory(), - Constants.MERGE_MSG)); - try { + try (FileOutputStream fos = new FileOutputStream( + new File(db.getDirectory(), Constants.MERGE_MSG))) { fos.write(mergeMsg.getBytes(Constants.CHARACTER_ENCODING)); - } finally { - fos.close(); } assertEquals(db.readMergeCommitMsg(), mergeMsg); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSerializerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSerializerTest.java new file mode 100644 index 0000000000..d98b792d75 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSerializerTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018, David Pursehouse <david.pursehouse@gmail.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.lib; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +import org.junit.Test; + +public class ObjectIdSerializerTest { + @Test + public void serialize() throws Exception { + ObjectId original = new ObjectId(1, 2, 3, 4, 5); + ObjectId deserialized = writeAndReadBackFromTempFile(original); + assertEquals(original, deserialized); + } + + @Test + public void serializeZeroId() throws Exception { + ObjectId original = ObjectId.zeroId(); + ObjectId deserialized = writeAndReadBackFromTempFile(original); + assertEquals(original, deserialized); + } + + @Test + public void serializeNull() throws Exception { + ObjectId deserialized = writeAndReadBackFromTempFile(null); + assertNull(deserialized); + } + + private ObjectId writeAndReadBackFromTempFile(ObjectId objectId) + throws Exception { + File file = File.createTempFile("ObjectIdSerializerTest_", ""); + try (OutputStream out = new FileOutputStream(file)) { + if (objectId == null) { + ObjectIdSerializer.write(out, objectId); + } else { + ObjectIdSerializer.writeWithoutMarker(out, objectId); + } + } + try (InputStream in = new FileInputStream(file)) { + if (objectId == null) { + return ObjectIdSerializer.read(in); + } else { + return ObjectIdSerializer.readWithoutMarker(in); + } + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java index 039a6e8cfc..190224a855 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java @@ -817,40 +817,35 @@ public class RecursiveMergerTest extends RepositoryTestCase { void modifyWorktree(WorktreeState worktreeState, String path, String other) throws Exception { - FileOutputStream fos = null; - ObjectId bloblId; - - try { - switch (worktreeState) { - case Missing: - new File(db.getWorkTree(), path).delete(); - break; - case DifferentFromHeadAndOther: - write(new File(db.getWorkTree(), path), - Integer.toString(counter++)); - break; - case SameAsHead: - bloblId = contentId(Constants.HEAD, path); - fos = new FileOutputStream(new File(db.getWorkTree(), path)); - db.newObjectReader().open(bloblId).copyTo(fos); - break; - case SameAsOther: - bloblId = contentId(other, path); - fos = new FileOutputStream(new File(db.getWorkTree(), path)); - db.newObjectReader().open(bloblId).copyTo(fos); - break; - case Bare: - if (db.isBare()) - return; - File workTreeFile = db.getWorkTree(); - db.getConfig().setBoolean("core", null, "bare", true); - db.getDirectory().renameTo(new File(workTreeFile, "test.git")); - db = new FileRepository(new File(workTreeFile, "test.git")); - db_t = new TestRepository<>(db); + switch (worktreeState) { + case Missing: + new File(db.getWorkTree(), path).delete(); + break; + case DifferentFromHeadAndOther: + write(new File(db.getWorkTree(), path), + Integer.toString(counter++)); + break; + case SameAsHead: + try (FileOutputStream fos = new FileOutputStream( + new File(db.getWorkTree(), path))) { + db.newObjectReader().open(contentId(Constants.HEAD, path)) + .copyTo(fos); } - } finally { - if (fos != null) - fos.close(); + break; + case SameAsOther: + try (FileOutputStream fos = new FileOutputStream( + new File(db.getWorkTree(), path))) { + db.newObjectReader().open(contentId(other, path)).copyTo(fos); + } + break; + case Bare: + if (db.isBare()) + return; + File workTreeFile = db.getWorkTree(); + db.getConfig().setBoolean("core", null, "bare", true); + db.getDirectory().renameTo(new File(workTreeFile, "test.git")); + db = new FileRepository(new File(workTreeFile, "test.git")); + db_t = new TestRepository<>(db); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java index 3272d598bc..9322a4734e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java @@ -46,6 +46,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; @@ -53,6 +55,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Arrays; +import java.util.Map; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.MergeResult; @@ -61,21 +64,29 @@ import org.eclipse.jgit.api.errors.CheckoutConflictException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheEditor; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.NoMergeBaseException; import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.ObjectStream; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FileUtils; @@ -387,6 +398,38 @@ public class ResolveMergerTest extends RepositoryTestCase { mergeResult.getMergeStatus()); } + @Theory + public void mergeWithCrlfAutoCrlfTrue(MergeStrategy strategy) + throws IOException, GitAPIException { + Git git = Git.wrap(db); + db.getConfig().setString("core", null, "autocrlf", "true"); + db.getConfig().save(); + writeTrashFile("crlf.txt", "a crlf file\r\n"); + git.add().addFilepattern("crlf.txt").call(); + git.commit().setMessage("base").call(); + + git.branchCreate().setName("brancha").call(); + + writeTrashFile("crlf.txt", "a crlf file\r\na second line\r\n"); + git.add().addFilepattern("crlf.txt").call(); + git.commit().setMessage("on master").call(); + + git.checkout().setName("brancha").call(); + File testFile = writeTrashFile("crlf.txt", + "a first line\r\na crlf file\r\n"); + git.add().addFilepattern("crlf.txt").call(); + git.commit().setMessage("on brancha").call(); + + MergeResult mergeResult = git.merge().setStrategy(strategy) + .include(db.resolve("master")).call(); + assertEquals(MergeResult.MergeStatus.MERGED, + mergeResult.getMergeStatus()); + checkFile(testFile, "a first line\r\na crlf file\r\na second line\r\n"); + assertEquals( + "[crlf.txt, mode:100644, content:a first line\na crlf file\na second line\n]", + indexState(CONTENT)); + } + /** * Merging two equal subtrees when the index does not contain any file in * that subtree should lead to a merged state. @@ -1098,6 +1141,146 @@ public class ResolveMergerTest extends RepositoryTestCase { indexState(CONTENT)); } + /** + * Merging two conflicting submodules when the index does not contain any + * entry for that submodule. + * + * @param strategy + * @throws Exception + */ + @Theory + public void checkMergeConflictingSubmodulesWithoutIndex( + MergeStrategy strategy) throws Exception { + Git git = Git.wrap(db); + writeTrashFile("initial", "initial"); + git.add().addFilepattern("initial").call(); + RevCommit initial = git.commit().setMessage("initial").call(); + + writeSubmodule("one", ObjectId + .fromString("1000000000000000000000000000000000000000")); + git.add().addFilepattern(Constants.DOT_GIT_MODULES).call(); + RevCommit right = git.commit().setMessage("added one").call(); + + // a second commit in the submodule + + git.checkout().setStartPoint(initial).setName("left") + .setCreateBranch(true).call(); + writeSubmodule("one", ObjectId + .fromString("2000000000000000000000000000000000000000")); + + git.add().addFilepattern(Constants.DOT_GIT_MODULES).call(); + git.commit().setMessage("a different one").call(); + + MergeResult result = git.merge().setStrategy(strategy).include(right) + .call(); + + assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); + Map<String, int[][]> conflicts = result.getConflicts(); + assertEquals(1, conflicts.size()); + assertNotNull(conflicts.get("one")); + } + + /** + * Merging two non-conflicting submodules when the index does not contain + * any entry for either submodule. + * + * @param strategy + * @throws Exception + */ + @Theory + public void checkMergeNonConflictingSubmodulesWithoutIndex( + MergeStrategy strategy) throws Exception { + Git git = Git.wrap(db); + writeTrashFile("initial", "initial"); + git.add().addFilepattern("initial").call(); + + writeSubmodule("one", ObjectId + .fromString("1000000000000000000000000000000000000000")); + + // Our initial commit should include a .gitmodules with a bunch of + // comment lines, so that + // we don't have a content merge issue when we add a new submodule at + // the top and a different + // one at the bottom. This is sort of a hack, but it should allow + // add/add submodule merges + String existing = read(Constants.DOT_GIT_MODULES); + String context = "\n# context\n# more context\n# yet more context\n"; + write(new File(db.getWorkTree(), Constants.DOT_GIT_MODULES), + existing + context + context + context); + + git.add().addFilepattern(Constants.DOT_GIT_MODULES).call(); + RevCommit initial = git.commit().setMessage("initial").call(); + + writeSubmodule("two", ObjectId + .fromString("1000000000000000000000000000000000000000")); + git.add().addFilepattern(Constants.DOT_GIT_MODULES).call(); + + RevCommit right = git.commit().setMessage("added two").call(); + + git.checkout().setStartPoint(initial).setName("left") + .setCreateBranch(true).call(); + + // we need to manually create the submodule for three for the + // .gitmodules hackery + addSubmoduleToIndex("three", ObjectId + .fromString("1000000000000000000000000000000000000000")); + new File(db.getWorkTree(), "three").mkdir(); + + existing = read(Constants.DOT_GIT_MODULES); + String three = "[submodule \"three\"]\n\tpath = three\n\turl = " + + db.getDirectory().toURI() + "\n"; + write(new File(db.getWorkTree(), Constants.DOT_GIT_MODULES), + three + existing); + + git.add().addFilepattern(Constants.DOT_GIT_MODULES).call(); + git.commit().setMessage("a different one").call(); + + MergeResult result = git.merge().setStrategy(strategy).include(right) + .call(); + + assertNull(result.getCheckoutConflicts()); + assertNull(result.getFailingPaths()); + for (String dir : Arrays.asList("one", "two", "three")) { + assertTrue(new File(db.getWorkTree(), dir).isDirectory()); + } + } + + private void writeSubmodule(String path, ObjectId commit) + throws IOException, ConfigInvalidException { + addSubmoduleToIndex(path, commit); + new File(db.getWorkTree(), path).mkdir(); + + StoredConfig config = db.getConfig(); + config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path, + ConfigConstants.CONFIG_KEY_URL, + db.getDirectory().toURI().toString()); + config.save(); + + FileBasedConfig modulesConfig = new FileBasedConfig( + new File(db.getWorkTree(), Constants.DOT_GIT_MODULES), + db.getFS()); + modulesConfig.load(); + modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path, + ConfigConstants.CONFIG_KEY_PATH, path); + modulesConfig.save(); + + } + + private void addSubmoduleToIndex(String path, ObjectId commit) + throws IOException { + DirCache cache = db.lockDirCache(); + DirCacheEditor editor = cache.editor(); + editor.add(new DirCacheEditor.PathEdit(path) { + + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.GITLINK); + ent.setObjectId(commit); + } + }); + editor.commit(); + } + // Assert that every specified index entry has the same last modification // timestamp as the associated file private void checkConsistentLastModified(String... pathes) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/EditListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/EditListTest.java index 61bd8cf9c2..6027aff716 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/EditListTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/EditListTest.java @@ -90,17 +90,14 @@ public class EditListTest { } private Patch parseTestPatchFile(final String patchFile) throws IOException { - final InputStream in = getClass().getResourceAsStream(patchFile); - if (in == null) { - fail("No " + patchFile + " test vector"); - return null; // Never happens - } - try { + try (InputStream in = getClass().getResourceAsStream(patchFile)) { + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } final Patch p = new Patch(); p.parse(in); return p; - } finally { - in.close(); } } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/GetTextTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/GetTextTest.java index 2aaf6afbe3..65375c7ae0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/GetTextTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/GetTextTest.java @@ -43,6 +43,8 @@ package org.eclipse.jgit.patch; +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -58,7 +60,7 @@ import org.junit.Test; public class GetTextTest { @Test public void testGetText_BothISO88591() throws IOException { - final Charset cs = Charset.forName("ISO-8859-1"); + final Charset cs = ISO_8859_1; final Patch p = parseTestPatchFile(); assertTrue(p.getErrors().isEmpty()); assertEquals(1, p.getFiles().size()); @@ -69,7 +71,7 @@ public class GetTextTest { @Test public void testGetText_NoBinary() throws IOException { - final Charset cs = Charset.forName("ISO-8859-1"); + final Charset cs = ISO_8859_1; final Patch p = parseTestPatchFile(); assertTrue(p.getErrors().isEmpty()); assertEquals(1, p.getFiles().size()); @@ -80,8 +82,8 @@ public class GetTextTest { @Test public void testGetText_Convert() throws IOException { - final Charset csOld = Charset.forName("ISO-8859-1"); - final Charset csNew = Charset.forName("UTF-8"); + final Charset csOld = ISO_8859_1; + final Charset csNew = UTF_8; final Patch p = parseTestPatchFile(); assertTrue(p.getErrors().isEmpty()); assertEquals(1, p.getFiles().size()); @@ -100,8 +102,8 @@ public class GetTextTest { @Test public void testGetText_DiffCc() throws IOException { - final Charset csOld = Charset.forName("ISO-8859-1"); - final Charset csNew = Charset.forName("UTF-8"); + final Charset csOld = ISO_8859_1; + final Charset csNew = UTF_8; final Patch p = parseTestPatchFile(); assertTrue(p.getErrors().isEmpty()); assertEquals(1, p.getFiles().size()); @@ -121,28 +123,24 @@ public class GetTextTest { private Patch parseTestPatchFile() throws IOException { final String patchFile = JGitTestUtil.getName() + ".patch"; - final InputStream in = getClass().getResourceAsStream(patchFile); - if (in == null) { - fail("No " + patchFile + " test vector"); - return null; // Never happens - } - try { + try (InputStream in = getClass().getResourceAsStream(patchFile)) { + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } final Patch p = new Patch(); p.parse(in); return p; - } finally { - in.close(); } } private String readTestPatchFile(final Charset cs) throws IOException { final String patchFile = JGitTestUtil.getName() + ".patch"; - final InputStream in = getClass().getResourceAsStream(patchFile); - if (in == null) { - fail("No " + patchFile + " test vector"); - return null; // Never happens - } - try { + try (InputStream in = getClass().getResourceAsStream(patchFile)) { + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } final InputStreamReader r = new InputStreamReader(in, cs); char[] tmp = new char[2048]; final StringBuilder s = new StringBuilder(); @@ -150,8 +148,6 @@ public class GetTextTest { while ((n = r.read(tmp)) > 0) s.append(tmp, 0, n); return s.toString(); - } finally { - in.close(); } } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java index 2a54dc6140..7f0d60295c 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java @@ -242,11 +242,8 @@ public class FileBasedConfigTest { dir.mkdirs(); File f = File.createTempFile(getClass().getName(), null, dir); - FileOutputStream os = new FileOutputStream(f, true); - try { + try (FileOutputStream os = new FileOutputStream(f, true)) { os.write(content); - } finally { - os.close(); } return f; } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java index b926e482f6..86c92bb528 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java @@ -60,6 +60,9 @@ import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.storage.pack.PackStatistics; +import org.eclipse.jgit.transport.BasePackFetchConnection.FetchConfig; import org.eclipse.jgit.transport.resolver.ReceivePackFactory; import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; import org.eclipse.jgit.transport.resolver.UploadPackFactory; @@ -70,6 +73,11 @@ import org.junit.Test; public class TestProtocolTest { private static final RefSpec HEADS = new RefSpec("+refs/heads/*:refs/heads/*"); + private static final RefSpec MASTER = new RefSpec( + "+refs/heads/master:refs/heads/master"); + + private static final int HAVES_PER_ROUND = 32; + private static class User { private final String name; @@ -81,7 +89,14 @@ public class TestProtocolTest { private static class DefaultUpload implements UploadPackFactory<User> { @Override public UploadPack create(User req, Repository db) { - return new UploadPack(db); + UploadPack up = new UploadPack(db); + up.setPostUploadHook(new PostUploadHook() { + @Override + public void onPostUpload(PackStatistics stats) { + havesCount = stats.getHaves(); + } + }); + return up; } } @@ -92,6 +107,8 @@ public class TestProtocolTest { } } + private static long havesCount; + private List<TransportProtocol> protos; private TestRepository<InMemoryRepository> local; private TestRepository<InMemoryRepository> remote; @@ -147,6 +164,68 @@ public class TestProtocolTest { } @Test + public void testFullNegotiation() throws Exception { + TestProtocol<User> proto = registerDefault(); + URIish uri = proto.register(new User("user"), remote.getRepository()); + + // Enough local branches to cause 10 rounds of negotiation, + // and a unique remote master branch commit with a later timestamp. + for (int i = 0; i < 10 * HAVES_PER_ROUND; i++) { + local.branch("local-branch-" + i).commit().create(); + } + remote.tick(11 * HAVES_PER_ROUND); + RevCommit master = remote.branch("master").commit() + .add("readme.txt", "unique commit").create(); + + try (Git git = new Git(local.getRepository())) { + assertNull(local.getRepository().exactRef("refs/heads/master")); + git.fetch().setRemote(uri.toString()).setRefSpecs(MASTER).call(); + assertEquals(master, local.getRepository() + .exactRef("refs/heads/master").getObjectId()); + assertEquals(10 * HAVES_PER_ROUND, havesCount); + } + } + + @Test + public void testMinimalNegotiation() throws Exception { + TestProtocol<User> proto = registerDefault(); + URIish uri = proto.register(new User("user"), remote.getRepository()); + + // Enough local branches to cause 10 rounds of negotiation, + // and a unique remote master branch commit with a later timestamp. + for (int i = 0; i < 10 * HAVES_PER_ROUND; i++) { + local.branch("local-branch-" + i).commit().create(); + } + remote.tick(11 * HAVES_PER_ROUND); + RevCommit master = remote.branch("master").commit() + .add("readme.txt", "unique commit").create(); + + TestProtocol.setFetchConfig(new FetchConfig(true, true)); + try (Git git = new Git(local.getRepository())) { + assertNull(local.getRepository().exactRef("refs/heads/master")); + git.fetch().setRemote(uri.toString()).setRefSpecs(MASTER).call(); + assertEquals(master, local.getRepository() + .exactRef("refs/heads/master").getObjectId()); + assertTrue(havesCount <= 2 * HAVES_PER_ROUND); + + // Update the remote master and add local branches for 5 more rounds + // of negotiation, where the local branch commits have newer + // timestamps. Negotiation should send 5 rounds for those newer + // branches before getting to the round that sends its stale version + // of master. + master = remote.branch("master").commit().parent(master).create(); + local.tick(2 * HAVES_PER_ROUND); + for (int i = 0; i < 5 * HAVES_PER_ROUND; i++) { + local.branch("local-" + i).commit().create(); + } + git.fetch().setRemote(uri.toString()).setRefSpecs(MASTER).call(); + assertEquals(master, local.getRepository() + .exactRef("refs/heads/master").getObjectId()); + assertEquals(6 * HAVES_PER_ROUND, havesCount); + } + } + + @Test public void testUploadPackFactory() throws Exception { ObjectId master = remote.branch("master").commit().create(); @@ -171,7 +250,7 @@ public class TestProtocolTest { try { git.fetch() .setRemote(user1Uri.toString()) - .setRefSpecs(HEADS) + .setRefSpecs(MASTER) .call(); } catch (InvalidRemoteException expected) { // Expected. @@ -181,7 +260,7 @@ public class TestProtocolTest { git.fetch() .setRemote(user2Uri.toString()) - .setRefSpecs(HEADS) + .setRefSpecs(MASTER) .call(); assertEquals(1, rejected.get()); assertEquals(master, diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java index 1eb218c865..39cd71947d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java @@ -1052,4 +1052,32 @@ public class URIishTest { assertEquals("", u.getPath()); assertEquals(str, u.toString()); } + + @Test + public void testEqualsHashcode() throws Exception + { + String[] urls = { "http://user:pass@example.com:8081/path", "../x", + "ssh://x.y:23/z", "ssh://example.com:/path", "D:\\m y", + "\\\\some\\place", "http://localhost:1234", + "user@example.com:some/p ath", "a", + "http://user:pwd@example.com:8081/path", + "http://user:pass@another.com:8081/path", + "http://user:pass@example.com:8083/path" }; + URIish w = new URIish("http://user:pass@example.com:8081/path/x"); + for (String s : urls) { + URIish u = new URIish(s); + URIish v = new URIish(s); + assertTrue(u.equals(v)); + assertTrue(v.equals(u)); + + assertFalse(u.equals(null)); + assertFalse(u.equals(new Object())); + assertFalse(new Object().equals(u)); + assertFalse(u.equals(w)); + assertFalse(w.equals(u)); + + assertTrue(u.hashCode() == v.hashCode()); + assertFalse(u.hashCode() == new Object().hashCode()); + } + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java index 1b434d3ddc..cb04f83fb5 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java @@ -43,7 +43,7 @@ package org.eclipse.jgit.transport; -import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.UTF_8; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.cryptoCipherListPBE; import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.cryptoCipherListTrans; import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.folderDelete; @@ -77,7 +77,6 @@ import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLConnection; import java.net.UnknownHostException; -import java.nio.charset.Charset; import java.nio.file.Files; import java.security.GeneralSecurityException; import java.security.Provider; @@ -353,8 +352,6 @@ public class WalkEncryptionTest { */ static class Util { - static final Charset UTF_8 = Charset.forName("UTF-8"); - /** * Read UTF-8 encoded text file into string. * diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java index 83a53b9a6d..1272e16173 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java @@ -93,25 +93,24 @@ public class AutoCRLFInputStreamTest { byte[] expectBytes = expect.getBytes(); for (int i = 0; i < 5; ++i) { byte[] buf = new byte[i]; - ByteArrayInputStream bis = new ByteArrayInputStream(inbytes); - InputStream in = new AutoCRLFInputStream(bis, true); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - if (i > 0) { - int n; - while ((n = in.read(buf)) >= 0) { - out.write(buf, 0, n); + try (ByteArrayInputStream bis = new ByteArrayInputStream(inbytes); + InputStream in = new AutoCRLFInputStream(bis, true); + ByteArrayOutputStream out = new ByteArrayOutputStream()) { + if (i > 0) { + int n; + while ((n = in.read(buf)) >= 0) { + out.write(buf, 0, n); + } + } else { + int c; + while ((c = in.read()) != -1) + out.write(c); } - } else { - int c; - while ((c = in.read()) != -1) - out.write(c); + out.flush(); + byte[] actualBytes = out.toByteArray(); + Assert.assertEquals("bufsize=" + i, encode(expectBytes), + encode(actualBytes)); } - out.flush(); - in.close(); - out.close(); - byte[] actualBytes = out.toByteArray(); - Assert.assertEquals("bufsize=" + i, encode(expectBytes), - encode(actualBytes)); } } diff --git a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs index ede0f7d55d..89394eca41 100644 --- a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF index d1709f82c7..d86fc7699a 100644 --- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF @@ -4,14 +4,14 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.ui Bundle-SymbolicName: org.eclipse.jgit.ui -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Vendor: %provider_name Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Export-Package: org.eclipse.jgit.awtui;version="4.10.1" -Import-Package: org.eclipse.jgit.errors;version="[4.10.1,4.11.0)", - org.eclipse.jgit.lib;version="[4.10.1,4.11.0)", - org.eclipse.jgit.nls;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revplot;version="[4.10.1,4.11.0)", - org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)", - org.eclipse.jgit.transport;version="[4.10.1,4.11.0)", - org.eclipse.jgit.util;version="[4.10.1,4.11.0)" +Export-Package: org.eclipse.jgit.awtui;version="4.11.2" +Import-Package: org.eclipse.jgit.errors;version="[4.11.2,4.12.0)", + org.eclipse.jgit.lib;version="[4.11.2,4.12.0)", + org.eclipse.jgit.nls;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revplot;version="[4.11.2,4.12.0)", + org.eclipse.jgit.revwalk;version="[4.11.2,4.12.0)", + org.eclipse.jgit.transport;version="[4.11.2,4.12.0)", + org.eclipse.jgit.util;version="[4.11.2,4.12.0)" diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml index 4979f08704..f20d4b4d20 100644 --- a/org.eclipse.jgit.ui/pom.xml +++ b/org.eclipse.jgit.ui/pom.xml @@ -52,7 +52,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.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 new file mode 100644 index 0000000000..d4a059110b --- /dev/null +++ b/org.eclipse.jgit/.settings/.api_filters @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<component id="org.eclipse.jgit" version="2"> + <resource path="META-INF/MANIFEST.MF"> + <filter id="924844039"> + <message_arguments> + <message_argument value="4.11.1"/> + <message_argument value="4.11.0"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/diff/DiffEntry.java" type="org.eclipse.jgit.diff.DiffEntry"> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.diff.DiffEntry"/> + <message_argument value="diffAttribute"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/lib/ConfigConstants.java" type="org.eclipse.jgit.lib.ConfigConstants"> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.lib.ConfigConstants"/> + <message_argument value="CONFIG_KEY_REQUIRED"/> + </message_arguments> + </filter> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.lib.ConfigConstants"/> + <message_argument value="CONFIG_SECTION_LFS"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/merge/ResolveMerger.java" type="org.eclipse.jgit.merge.ResolveMerger"> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/> + <message_argument value="workingTreeOptions"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/storage/pack/PackStatistics.java" type="org.eclipse.jgit.storage.pack.PackStatistics$Accumulator"> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/> + <message_argument value="advertised"/> + </message_arguments> + </filter> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/> + <message_argument value="haves"/> + </message_arguments> + </filter> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/> + <message_argument value="timeNegotiating"/> + </message_arguments> + </filter> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/> + <message_argument value="wants"/> + </message_arguments> + </filter> + </resource> +</component> diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs index 06ddbabb48..13c32a6d94 100644 --- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs @@ -25,7 +25,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD index a8a53f2742..6ba7796b7e 100644 --- a/org.eclipse.jgit/BUILD +++ b/org.eclipse.jgit/BUILD @@ -14,12 +14,17 @@ RESOURCES = glob(["resources/**"]) java_library( name = "jgit", srcs = SRCS, + javacopts = select({ + "//:jdk9": ["--add-modules=java.xml.bind"], + "//conditions:default": [], + }), resource_strip_prefix = "org.eclipse.jgit/resources", resources = RESOURCES, deps = [ ":insecure_cipher_factory", "//lib:javaewah", "//lib:jsch", + "//lib:jzlib", "//lib:slf4j-api", ], ) diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index b2d7c8e91a..b6268169d6 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -3,12 +3,12 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit Bundle-SymbolicName: org.eclipse.jgit -Bundle-Version: 4.10.1.qualifier +Bundle-Version: 4.11.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy -Export-Package: org.eclipse.jgit.annotations;version="4.10.1", - org.eclipse.jgit.api;version="4.10.1"; +Export-Package: org.eclipse.jgit.annotations;version="4.11.2", + org.eclipse.jgit.api;version="4.11.2"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff, @@ -22,52 +22,52 @@ Export-Package: org.eclipse.jgit.annotations;version="4.10.1", org.eclipse.jgit.submodule, org.eclipse.jgit.transport, org.eclipse.jgit.merge", - org.eclipse.jgit.api.errors;version="4.10.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors", - org.eclipse.jgit.attributes;version="4.10.1", - org.eclipse.jgit.blame;version="4.10.1"; + org.eclipse.jgit.api.errors;version="4.11.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors", + org.eclipse.jgit.attributes;version="4.11.2", + org.eclipse.jgit.blame;version="4.11.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.10.1"; + org.eclipse.jgit.diff;version="4.11.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.10.1"; + org.eclipse.jgit.dircache;version="4.11.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.10.1"; + org.eclipse.jgit.errors;version="4.11.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.10.1";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.fnmatch;version="4.10.1", - org.eclipse.jgit.gitrepo;version="4.10.1"; + org.eclipse.jgit.events;version="4.11.2";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.fnmatch;version="4.11.2", + org.eclipse.jgit.gitrepo;version="4.11.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.10.1";x-internal:=true, - org.eclipse.jgit.hooks;version="4.10.1";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.ignore;version="4.10.1", - org.eclipse.jgit.ignore.internal;version="4.10.1";x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal;version="4.10.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", - org.eclipse.jgit.internal.fsck;version="4.10.1";x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.ketch;version="4.10.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.dfs;version="4.10.1"; + org.eclipse.jgit.gitrepo.internal;version="4.11.2";x-internal:=true, + org.eclipse.jgit.hooks;version="4.11.2";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.ignore;version="4.11.2", + org.eclipse.jgit.ignore.internal;version="4.11.2";x-friends:="org.eclipse.jgit.test", + org.eclipse.jgit.internal;version="4.11.2";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", + org.eclipse.jgit.internal.fsck;version="4.11.2";x-friends:="org.eclipse.jgit.test", + org.eclipse.jgit.internal.ketch;version="4.11.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.storage.dfs;version="4.11.2"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.http.server, org.eclipse.jgit.http.test, org.eclipse.jgit.lfs.test", - org.eclipse.jgit.internal.storage.file;version="4.10.1"; + org.eclipse.jgit.internal.storage.file;version="4.11.2"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.junit, org.eclipse.jgit.junit.http, @@ -75,12 +75,12 @@ Export-Package: org.eclipse.jgit.annotations;version="4.10.1", org.eclipse.jgit.lfs, org.eclipse.jgit.pgm, org.eclipse.jgit.pgm.test", - org.eclipse.jgit.internal.storage.io;version="4.10.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.pack;version="4.10.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.reftable;version="4.10.1"; + org.eclipse.jgit.internal.storage.io;version="4.11.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.storage.pack;version="4.11.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.storage.reftable;version="4.11.2"; x-friends:="org.eclipse.jgit.http.test,org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.reftree;version="4.10.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.lib;version="4.10.1"; + org.eclipse.jgit.internal.storage.reftree;version="4.11.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.lib;version="4.11.2"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util, @@ -90,33 +90,33 @@ Export-Package: org.eclipse.jgit.annotations;version="4.10.1", org.eclipse.jgit.treewalk, org.eclipse.jgit.transport, org.eclipse.jgit.submodule", - org.eclipse.jgit.lib.internal;version="4.10.1";x-internal:=true, - org.eclipse.jgit.merge;version="4.10.1"; + org.eclipse.jgit.lib.internal;version="4.11.2";x-internal:=true, + org.eclipse.jgit.merge;version="4.11.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.10.1", - org.eclipse.jgit.notes;version="4.10.1"; + org.eclipse.jgit.nls;version="4.11.2", + org.eclipse.jgit.notes;version="4.11.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.merge", - org.eclipse.jgit.patch;version="4.10.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff", - org.eclipse.jgit.revplot;version="4.10.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk", - org.eclipse.jgit.revwalk;version="4.10.1"; + org.eclipse.jgit.patch;version="4.11.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff", + org.eclipse.jgit.revplot;version="4.11.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk", + org.eclipse.jgit.revwalk;version="4.11.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.10.1";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util", - org.eclipse.jgit.storage.file;version="4.10.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util", - org.eclipse.jgit.storage.pack;version="4.10.1";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.submodule;version="4.10.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk", - org.eclipse.jgit.transport;version="4.10.1"; + org.eclipse.jgit.revwalk.filter;version="4.11.2";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util", + org.eclipse.jgit.storage.file;version="4.11.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util", + org.eclipse.jgit.storage.pack;version="4.11.2";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.submodule;version="4.11.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk", + org.eclipse.jgit.transport;version="4.11.2"; uses:="org.eclipse.jgit.transport.resolver, org.eclipse.jgit.revwalk, org.eclipse.jgit.internal.storage.pack, @@ -128,24 +128,24 @@ Export-Package: org.eclipse.jgit.annotations;version="4.10.1", org.eclipse.jgit.transport.http, org.eclipse.jgit.errors, org.eclipse.jgit.storage.pack", - org.eclipse.jgit.transport.http;version="4.10.1";uses:="javax.net.ssl", - org.eclipse.jgit.transport.resolver;version="4.10.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport", - org.eclipse.jgit.treewalk;version="4.10.1"; + org.eclipse.jgit.transport.http;version="4.11.2";uses:="javax.net.ssl", + org.eclipse.jgit.transport.resolver;version="4.11.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport", + org.eclipse.jgit.treewalk;version="4.11.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.10.1";uses:="org.eclipse.jgit.treewalk", - org.eclipse.jgit.util;version="4.10.1"; + org.eclipse.jgit.treewalk.filter;version="4.11.2";uses:="org.eclipse.jgit.treewalk", + org.eclipse.jgit.util;version="4.11.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.10.1", - org.eclipse.jgit.util.sha1;version="4.10.1", - org.eclipse.jgit.util.time;version="4.10.1" + org.eclipse.jgit.util.io;version="4.11.2", + org.eclipse.jgit.util.sha1;version="4.11.2", + org.eclipse.jgit.util.time;version="4.11.2" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)", com.jcraft.jsch;version="[0.1.37,0.2.0)", diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF index ec2a99a863..bbd66fddf6 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.10.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit;version="4.10.1.qualifier";roots="." +Bundle-Version: 4.11.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit;version="4.11.2.qualifier";roots="." diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index a0d8a126de..cb05734c63 100644 --- a/org.eclipse.jgit/pom.xml +++ b/org.eclipse.jgit/pom.xml @@ -53,7 +53,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit</artifactId> @@ -75,6 +75,11 @@ </dependency> <dependency> + <groupId>com.jcraft</groupId> + <artifactId>jzlib</artifactId> + </dependency> + + <dependency> <groupId>com.googlecode.javaewah</groupId> <artifactId>JavaEWAH</artifactId> </dependency> diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index 0342bad95a..0d8ba01e22 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -41,6 +41,8 @@ bitmapsMustBePrepared=Bitmaps must be prepared before they may be written. blameNotCommittedYet=Not Committed Yet blobNotFound=Blob not found: {0} blobNotFoundForPath=Blob not found: {0} for path: {1} +blockLimitNotMultipleOfBlockSize=blockLimit {0} must be a multiple of blockSize {1} +blockLimitNotPositive=blockLimit must be positive: {0} blockSizeNotPowerOf2=blockSize must be a power of 2 bothRefTargetsMustNotBeNull=both old and new ref targets must not be null. branchNameInvalid=Branch name {0} is not allowed @@ -116,6 +118,7 @@ cantFindObjectInReversePackIndexForTheSpecifiedOffset=Can''t find object in (rev cantPassMeATree=Can't pass me a tree! channelMustBeInRange1_255=channel {0} must be in range [1, 255] characterClassIsNotSupported=The character class {0} is not supported. +checkingOutFiles=Checking out files checkoutConflictWithFile=Checkout conflict with file: {0} checkoutConflictWithFiles=Checkout conflict with files: {0} checkoutUnexpectedResult=Checkout returned unexpected result {0} @@ -367,6 +370,7 @@ invalidIgnoreRule=Exception caught while parsing ignore rule ''{0}''. invalidIntegerValue=Invalid integer value: {0}.{1}={2} invalidKey=Invalid key: {0} invalidLineInConfigFile=Invalid line in config file +invalidLineInConfigFileWithParam=Invalid line in config file: {0} invalidModeFor=Invalid mode {0} for {1} {2} in {3}. invalidModeForPath=Invalid mode {0} for path {1} invalidObject=Invalid {0} {1}: {2} @@ -405,6 +409,7 @@ largeObjectExceedsLimit=Object {0} exceeds {1} limit, actual size is {2} largeObjectException={0} exceeds size limit largeObjectOutOfMemory=Out of memory loading {0} lengthExceedsMaximumArraySize=Length exceeds maximum array size +lfsHookConflict=LFS built-in hook conflicts with existing pre-push hook in repository {0}. Either remove the pre-push hook or disable built-in LFS support. listingAlternates=Listing alternates listingPacks=Listing packs localObjectsIncomplete=Local objects incomplete. @@ -459,6 +464,7 @@ noHMACsupport=No {0} support: {1} noMergeBase=No merge base could be determined. Reason={0}. {1} noMergeHeadSpecified=No merge head specified nonBareLinkFilesNotSupported=Link files are not supported with nonbare repos +noPathAttributesFound=No Attributes found for {0}. noSuchRef=no such ref noSuchSubmodule=no such submodule {0} notABoolean=Not a boolean: {0} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java index 08f81cb9f8..1cc65c9f45 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java @@ -56,7 +56,7 @@ import java.lang.annotation.Target; /** * JGit's replacement for the {@code javax.annotation.Nonnull}. * <p> - * Denotes that a local variable, parameter, field, method return value expected + * Denotes that a local variable, parameter, field, method return value is expected * to be non {@code null}. * * @since 4.2 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java index f80c8c76ed..688f5f4f01 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java @@ -99,7 +99,7 @@ public class AddNoteCommand extends GitCommand<Note> { map = NoteMap.read(walk.getObjectReader(), notesCommit); } map.set(id, message, inserter); - commitNoteMap(walk, map, notesCommit, inserter, + commitNoteMap(repo, notesRef, walk, map, notesCommit, inserter, "Notes added by 'git notes add'"); //$NON-NLS-1$ return map.getNote(id); } catch (IOException e) { @@ -134,7 +134,8 @@ public class AddNoteCommand extends GitCommand<Note> { return this; } - private void commitNoteMap(RevWalk walk, NoteMap map, + static void commitNoteMap(Repository r, String ref, RevWalk walk, + NoteMap map, RevCommit notesCommit, ObjectInserter inserter, String msg) @@ -142,14 +143,14 @@ public class AddNoteCommand extends GitCommand<Note> { // commit the note CommitBuilder builder = new CommitBuilder(); builder.setTreeId(map.writeTree(inserter)); - builder.setAuthor(new PersonIdent(repo)); + builder.setAuthor(new PersonIdent(r)); builder.setCommitter(builder.getAuthor()); builder.setMessage(msg); if (notesCommit != null) builder.setParentIds(notesCommit); ObjectId commit = inserter.insert(builder); inserter.flush(); - RefUpdate refUpdate = repo.updateRef(notesRef); + RefUpdate refUpdate = r.updateRef(ref); if (notesCommit != null) refUpdate.setExpectedOldObjectId(notesCommit); else diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java index cfc55d8cc6..5b84032b15 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java @@ -258,9 +258,9 @@ public class ApplyCommand extends GitCommand<ApplyResult> { if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); } - FileWriter fw = new FileWriter(f); - fw.write(sb.toString()); - fw.close(); + try (FileWriter fw = new FileWriter(f)) { + fw.write(sb.toString()); + } getRepository().getFS().setExecute(f, fh.getNewMode() == FileMode.EXECUTABLE_FILE); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java index c29ed0e22d..10397f8ac9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java @@ -392,9 +392,10 @@ public class ArchiveCommand extends GitCommand<OutputStream> { private <T extends Closeable> OutputStream writeArchive(Format<T> fmt) { try { try (TreeWalk walk = new TreeWalk(repo); - RevWalk rw = new RevWalk(walk.getObjectReader())) { + RevWalk rw = new RevWalk(walk.getObjectReader()); + T outa = fmt.createArchiveOutputStream(out, + formatOptions)) { String pfx = prefix == null ? "" : prefix; //$NON-NLS-1$ - T outa = fmt.createArchiveOutputStream(out, formatOptions); MutableObjectId idBuf = new MutableObjectId(); ObjectReader reader = walk.getObjectReader(); @@ -427,7 +428,6 @@ public class ArchiveCommand extends GitCommand<OutputStream> { walk.getObjectId(idBuf, 0); fmt.putEntry(outa, tree, name, mode, reader.open(idBuf)); } - outa.close(); return out; } finally { out.close(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java index aa9939edd3..11edb10894 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java @@ -76,8 +76,10 @@ import org.eclipse.jgit.lib.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.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.RefUpdate.Result; @@ -182,6 +184,8 @@ public class CheckoutCommand extends GitCommand<Ref> { private Set<String> actuallyModifiedPaths; + private ProgressMonitor monitor = NullProgressMonitor.INSTANCE; + /** * Constructor for CheckoutCommand * @@ -266,6 +270,7 @@ public class CheckoutCommand extends GitCommand<Ref> { dco = new DirCacheCheckout(repo, headTree, dc, newCommit.getTree()); dco.setFailOnConflict(true); + dco.setProgressMonitor(monitor); try { dco.checkout(); } catch (org.eclipse.jgit.errors.CheckoutConflictException e) { @@ -347,6 +352,20 @@ public class CheckoutCommand extends GitCommand<Ref> { } /** + * @param monitor + * a progress monitor + * @return this instance + * @since 4.11 + */ + public CheckoutCommand setProgressMonitor(ProgressMonitor monitor) { + if (monitor == null) { + monitor = NullProgressMonitor.INSTANCE; + } + this.monitor = monitor; + return this; + } + + /** * Add a single slash-separated path to the list of paths to check out. To * check out all paths, use {@link #setAllPaths(boolean)}. * <p> diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java index 771798a50d..f45e39e069 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java @@ -60,8 +60,10 @@ import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; +import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref.Storage; import org.eclipse.jgit.lib.Repository; @@ -95,6 +97,8 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> { private boolean noCommit = false; + private ProgressMonitor monitor = NullProgressMonitor.INSTANCE; + /** * Constructor for CherryPickCommand * @@ -160,6 +164,7 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> { newHead.getTree(), repo.lockDirCache(), merger.getResultTreeId()); dco.setFailOnConflict(true); + dco.setProgressMonitor(monitor); dco.checkout(); if (!noCommit) newHead = new Git(getRepository()).commit() @@ -332,6 +337,24 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> { return this; } + /** + * The progress monitor associated with the cherry-pick operation. By + * default, this is set to <code>NullProgressMonitor</code> + * + * @see NullProgressMonitor + * @param monitor + * a {@link org.eclipse.jgit.lib.ProgressMonitor} + * @return {@code this} + * @since 4.11 + */ + public CherryPickCommand setProgressMonitor(ProgressMonitor monitor) { + if (monitor == null) { + monitor = NullProgressMonitor.INSTANCE; + } + this.monitor = monitor; + return this; + } + private String calculateOurName(Ref headRef) { if (ourCommitName != null) return ourCommitName; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java index c81425067c..0d9fe41afb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java @@ -103,27 +103,25 @@ public class CleanCommand extends GitCommand<Set<String>> { StatusCommand command = new StatusCommand(repo); Status status = command.call(); - Set<String> untrackedAndIgnoredFiles = new TreeSet<>( - status.getUntracked()); - Set<String> untrackedAndIgnoredDirs = new TreeSet<>( + Set<String> untrackedFiles = new TreeSet<>(status.getUntracked()); + Set<String> untrackedDirs = new TreeSet<>( status.getUntrackedFolders()); FS fs = getRepository().getFS(); for (String p : status.getIgnoredNotInIndex()) { File f = new File(repo.getWorkTree(), p); - if (fs.isFile(f) || fs.isSymLink(f)) - untrackedAndIgnoredFiles.add(p); - else if (fs.isDirectory(f)) - untrackedAndIgnoredDirs.add(p); + if (fs.isFile(f) || fs.isSymLink(f)) { + untrackedFiles.add(p); + } else if (fs.isDirectory(f)) { + untrackedDirs.add(p); + } } - Set<String> filtered = filterFolders(untrackedAndIgnoredFiles, - untrackedAndIgnoredDirs); + Set<String> filtered = filterFolders(untrackedFiles, untrackedDirs); Set<String> notIgnoredFiles = filterIgnorePaths(filtered, status.getIgnoredNotInIndex(), true); - Set<String> notIgnoredDirs = filterIgnorePaths( - untrackedAndIgnoredDirs, + Set<String> notIgnoredDirs = filterIgnorePaths(untrackedDirs, status.getIgnoredNotInIndex(), false); for (String file : notIgnoredFiles) @@ -174,20 +172,22 @@ public class CleanCommand extends GitCommand<Set<String>> { if (new File(curFile, DOT_GIT).exists()) { if (force) { if (!dryRun) { - FileUtils.delete(curFile, FileUtils.RECURSIVE); + FileUtils.delete(curFile, FileUtils.RECURSIVE + | FileUtils.SKIP_MISSING); } inFiles.add(path + "/"); //$NON-NLS-1$ } } else { if (!dryRun) { - FileUtils.delete(curFile, FileUtils.RECURSIVE); + FileUtils.delete(curFile, + FileUtils.RECURSIVE | FileUtils.SKIP_MISSING); } inFiles.add(path + "/"); //$NON-NLS-1$ } } } else { if (!dryRun) { - FileUtils.delete(curFile, FileUtils.NONE); + FileUtils.delete(curFile, FileUtils.SKIP_MISSING); } inFiles.add(path); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java index 6d3afc619a..79b0efbe6d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java @@ -361,6 +361,7 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> { DirCache dc = clonedRepo.lockDirCache(); DirCacheCheckout co = new DirCacheCheckout(clonedRepo, dc, commit.getTree()); + co.setProgressMonitor(monitor); co.checkout(); if (cloneSubmodules) cloneSubmodules(clonedRepo); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java index 8a89ba1611..f2572835fc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -409,14 +409,11 @@ public class CommitCommand extends GitCommand<RevCommit> { inserter = repo.newObjectInserter(); long contentLength = fTree .getEntryContentLength(); - InputStream inputStream = fTree - .openEntryStream(); - try { + try (InputStream inputStream = fTree + .openEntryStream()) { dcEntry.setObjectId(inserter.insert( Constants.OBJ_BLOB, contentLength, inputStream)); - } finally { - inputStream.close(); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java index 4c6f351142..f65b5735de 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java @@ -104,6 +104,12 @@ public class DiffCommand extends GitCommand<List<DiffEntry>> { super(repo); } + private DiffFormatter getDiffFormatter() { + return out != null && !showNameAndStatusOnly + ? new DiffFormatter(new BufferedOutputStream(out)) + : new DiffFormatter(NullOutputStream.INSTANCE); + } + /** * {@inheritDoc} * <p> @@ -114,14 +120,9 @@ public class DiffCommand extends GitCommand<List<DiffEntry>> { */ @Override public List<DiffEntry> call() throws GitAPIException { - final DiffFormatter diffFmt; - if (out != null && !showNameAndStatusOnly) - diffFmt = new DiffFormatter(new BufferedOutputStream(out)); - else - diffFmt = new DiffFormatter(NullOutputStream.INSTANCE); - diffFmt.setRepository(repo); - diffFmt.setProgressMonitor(monitor); - try { + try (DiffFormatter diffFmt = getDiffFormatter()) { + diffFmt.setRepository(repo); + diffFmt.setProgressMonitor(monitor); if (cached) { if (oldTree == null) { ObjectId head = repo.resolve(HEAD + "^{tree}"); //$NON-NLS-1$ @@ -159,8 +160,6 @@ public class DiffCommand extends GitCommand<List<DiffEntry>> { } } catch (IOException e) { throw new JGitInternalException(e.getMessage(), e); - } finally { - diffFmt.close(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java index b2c28dab0c..5d178bc13c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java @@ -171,38 +171,42 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> { } walk.setTree(revWalk.parseTree(fetchHead)); while (walk.next()) { - Repository submoduleRepo = walk.getRepository(); - - // Skip submodules that don't exist locally (have not been - // cloned), are not registered in the .gitmodules file, or - // not registered in the parent repository's config. - if (submoduleRepo == null || walk.getModulesPath() == null - || walk.getConfigUrl() == null) { - continue; - } + try (Repository submoduleRepo = walk.getRepository()) { + + // Skip submodules that don't exist locally (have not been + // cloned), are not registered in the .gitmodules file, or + // not registered in the parent repository's config. + if (submoduleRepo == null || walk.getModulesPath() == null + || walk.getConfigUrl() == null) { + continue; + } - FetchRecurseSubmodulesMode recurseMode = getRecurseMode( - walk.getPath()); - - // When the fetch mode is "yes" we always fetch. When the mode - // is "on demand", we only fetch if the submodule's revision was - // updated to an object that is not currently present in the - // submodule. - if ((recurseMode == FetchRecurseSubmodulesMode.ON_DEMAND - && !submoduleRepo.hasObject(walk.getObjectId())) - || recurseMode == FetchRecurseSubmodulesMode.YES) { - FetchCommand f = new FetchCommand(submoduleRepo) - .setProgressMonitor(monitor).setTagOpt(tagOption) - .setCheckFetchedObjects(checkFetchedObjects) - .setRemoveDeletedRefs(isRemoveDeletedRefs()) - .setThin(thin).setRefSpecs(refSpecs) - .setDryRun(dryRun) - .setRecurseSubmodules(recurseMode); - configure(f); - if (callback != null) { - callback.fetchingSubmodule(walk.getPath()); + FetchRecurseSubmodulesMode recurseMode = getRecurseMode( + walk.getPath()); + + // When the fetch mode is "yes" we always fetch. When the + // mode + // is "on demand", we only fetch if the submodule's revision + // was + // updated to an object that is not currently present in the + // submodule. + if ((recurseMode == FetchRecurseSubmodulesMode.ON_DEMAND + && !submoduleRepo.hasObject(walk.getObjectId())) + || recurseMode == FetchRecurseSubmodulesMode.YES) { + FetchCommand f = new FetchCommand(submoduleRepo) + .setProgressMonitor(monitor) + .setTagOpt(tagOption) + .setCheckFetchedObjects(checkFetchedObjects) + .setRemoveDeletedRefs(isRemoveDeletedRefs()) + .setThin(thin).setRefSpecs(refSpecs) + .setDryRun(dryRun) + .setRecurseSubmodules(recurseMode); + configure(f); + if (callback != null) { + callback.fetchingSubmodule(walk.getPath()); + } + results.addSubmodule(walk.getPath(), f.call()); } - results.addSubmodule(walk.getPath(), f.call()); } } } catch (IOException e) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java index f10bcdfcde..d48049f1ed 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java @@ -75,6 +75,9 @@ public class InitCommand implements Callable<Git> { * {@inheritDoc} * <p> * Executes the {@code Init} command. + * + * @return a {@code Git} instance that owns the {@code Repository} that it + * wraps. */ @Override public Git call() throws GitAPIException { @@ -120,7 +123,7 @@ public class InitCommand implements Callable<Git> { Repository repository = builder.build(); if (!repository.getObjectDatabase().exists()) repository.create(bare); - return new Git(repository); + return new Git(repository, true); } catch (IOException e) { throw new JGitInternalException(e.getMessage(), e); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java index 44ff18fcc4..cd50cae4fb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java @@ -237,9 +237,8 @@ public class MergeCommand extends GitCommand<MergeResult> { fallBackToConfiguration(); checkParameters(); - RevWalk revWalk = null; DirCacheCheckout dco = null; - try { + try (RevWalk revWalk = new RevWalk(repo)) { Ref head = repo.exactRef(Constants.HEAD); if (head == null) throw new NoHeadException( @@ -247,7 +246,6 @@ public class MergeCommand extends GitCommand<MergeResult> { StringBuilder refLogMessage = new StringBuilder("merge "); //$NON-NLS-1$ // Check for FAST_FORWARD, ALREADY_UP_TO_DATE - revWalk = new RevWalk(repo); // we know for now there is only one commit Ref ref = commits.get(0); @@ -268,6 +266,7 @@ public class MergeCommand extends GitCommand<MergeResult> { dco = new DirCacheCheckout(repo, repo.lockDirCache(), srcCommit.getTree()); dco.setFailOnConflict(true); + dco.setProgressMonitor(monitor); dco.checkout(); RefUpdate refUpdate = repo .updateRef(head.getTarget().getName()); @@ -298,6 +297,7 @@ public class MergeCommand extends GitCommand<MergeResult> { dco = new DirCacheCheckout(repo, headCommit.getTree(), repo.lockDirCache(), srcCommit.getTree()); + dco.setProgressMonitor(monitor); dco.setFailOnConflict(true); dco.checkout(); String msg = null; @@ -376,6 +376,7 @@ public class MergeCommand extends GitCommand<MergeResult> { headCommit.getTree(), repo.lockDirCache(), merger.getResultTreeId()); dco.setFailOnConflict(true); + dco.setProgressMonitor(monitor); dco.checkout(); String msg = null; @@ -436,9 +437,6 @@ public class MergeCommand extends GitCommand<MergeResult> { MessageFormat.format( JGitText.get().exceptionCaughtDuringExecutionOfMergeCommand, e), e); - } finally { - if (revWalk != null) - revWalk.close(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java index b86a2fdf8e..da1ff06ae5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -932,6 +932,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { try { DirCacheCheckout dco = new DirCacheCheckout(repo, dc, headTree); dco.setFailOnConflict(false); + dco.setProgressMonitor(monitor); boolean needsDeleteFiles = dco.checkout(); if (needsDeleteFiles) { List<String> fileList = dco.getToBeDeleted(); @@ -1265,6 +1266,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { CheckoutCommand co = new CheckoutCommand(repo); try { + co.setProgressMonitor(monitor); co.setName(newCommit.name()).call(); if (headName.startsWith(Constants.R_HEADS)) { RefUpdate rup = repo.updateRef(headName); @@ -1407,6 +1409,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { DirCacheCheckout dco = new DirCacheCheckout(repo, head.getTree(), repo.lockDirCache(), commit.getTree()); dco.setFailOnConflict(true); + dco.setProgressMonitor(monitor); try { dco.checkout(); } catch (org.eclipse.jgit.errors.CheckoutConflictException cce) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java index baae8248f3..cfbf0dd4e7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java @@ -46,13 +46,9 @@ import java.io.IOException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.JGitInternalException; -import org.eclipse.jgit.lib.CommitBuilder; import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; -import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.notes.Note; import org.eclipse.jgit.notes.NoteMap; @@ -99,7 +95,8 @@ public class RemoveNoteCommand extends GitCommand<Note> { map = NoteMap.read(walk.getObjectReader(), notesCommit); } map.set(id, null, inserter); - commitNoteMap(walk, map, notesCommit, inserter, + AddNoteCommand.commitNoteMap(repo, notesRef, walk, map, notesCommit, + inserter, "Notes removed by 'git notes remove'"); //$NON-NLS-1$ return map.getNote(id); } catch (IOException e) { @@ -121,30 +118,6 @@ public class RemoveNoteCommand extends GitCommand<Note> { return this; } - private void commitNoteMap(RevWalk walk, NoteMap map, - RevCommit notesCommit, - ObjectInserter inserter, - String msg) - throws IOException { - // commit the note - CommitBuilder builder = new CommitBuilder(); - builder.setTreeId(map.writeTree(inserter)); - builder.setAuthor(new PersonIdent(repo)); - builder.setCommitter(builder.getAuthor()); - builder.setMessage(msg); - if (notesCommit != null) - builder.setParentIds(notesCommit); - ObjectId commit = inserter.insert(builder); - inserter.flush(); - RefUpdate refUpdate = repo.updateRef(notesRef); - if (notesCommit != null) - refUpdate.setExpectedOldObjectId(notesCommit); - else - refUpdate.setExpectedOldObjectId(ObjectId.zeroId()); - refUpdate.setNewObjectId(commit); - refUpdate.update(walk); - } - /** * Set the name of the <code>Ref</code> to remove a note from. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java index 86a69b019d..be446f9447 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java @@ -58,7 +58,9 @@ import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; @@ -125,6 +127,8 @@ public class ResetCommand extends GitCommand<Ref> { private boolean isReflogDisabled; + private ProgressMonitor monitor = NullProgressMonitor.INSTANCE; + /** * <p> * Constructor for ResetCommand. @@ -336,6 +340,24 @@ public class ResetCommand extends GitCommand<Ref> { return Constants.HEAD; } + /** + * The progress monitor associated with the reset operation. By default, + * this is set to <code>NullProgressMonitor</code> + * + * @see NullProgressMonitor + * @param monitor + * a {@link org.eclipse.jgit.lib.ProgressMonitor} + * @return {@code this} + * @since 4.11 + */ + public ResetCommand setProgressMonitor(ProgressMonitor monitor) { + if (monitor == null) { + monitor = NullProgressMonitor.INSTANCE; + } + this.monitor = monitor; + return this; + } + private void resetIndexForPaths(ObjectId commitTree) { DirCache dc = null; try (final TreeWalk tw = new TreeWalk(repo)) { @@ -420,6 +442,7 @@ public class ResetCommand extends GitCommand<Ref> { DirCacheCheckout checkout = new DirCacheCheckout(repo, dc, commitTree); checkout.setFailOnConflict(false); + checkout.setProgressMonitor(monitor); try { checkout.checkout(); } catch (org.eclipse.jgit.errors.CheckoutConflictException cce) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java index fa0d4c4882..46e0df7263 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java @@ -61,8 +61,10 @@ import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; +import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref.Storage; import org.eclipse.jgit.lib.Repository; @@ -97,6 +99,8 @@ public class RevertCommand extends GitCommand<RevCommit> { private MergeStrategy strategy = MergeStrategy.RECURSIVE; + private ProgressMonitor monitor = NullProgressMonitor.INSTANCE; + /** * <p> * Constructor for RevertCommand. @@ -178,6 +182,7 @@ public class RevertCommand extends GitCommand<RevCommit> { headCommit.getTree(), repo.lockDirCache(), merger.getResultTreeId()); dco.setFailOnConflict(true); + dco.setProgressMonitor(monitor); dco.checkout(); try (Git git = new Git(getRepository())) { newHead = git.commit().setMessage(newMessage) @@ -325,4 +330,22 @@ public class RevertCommand extends GitCommand<RevCommit> { this.strategy = strategy; return this; } + + /** + * The progress monitor associated with the revert operation. By default, + * this is set to <code>NullProgressMonitor</code> + * + * @see NullProgressMonitor + * @param monitor + * a {@link org.eclipse.jgit.lib.ProgressMonitor} + * @return {@code this} + * @since 4.11 + */ + public RevertCommand setProgressMonitor(ProgressMonitor monitor) { + if (monitor == null) { + monitor = NullProgressMonitor.INSTANCE; + } + this.monitor = monitor; + return this; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java index 4b4e18c134..3362d46f10 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java @@ -226,6 +226,7 @@ public class SubmoduleUpdateCommand extends submoduleRepo, submoduleRepo.lockDirCache(), commit.getTree()); co.setFailOnConflict(true); + co.setProgressMonitor(monitor); co.checkout(); RefUpdate refUpdate = submoduleRepo.updateRef( Constants.HEAD, true); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java index aae00764f6..c4357d1297 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java @@ -67,6 +67,9 @@ public abstract class FilterCommand { /** * Constructor for FilterCommand + * <p> + * FilterCommand implementors are required to manage the in and out streams + * (close on success and/or exception). * * @param in * The {@link java.io.InputStream} this command should read from @@ -84,6 +87,9 @@ public abstract class FilterCommand { * number of bytes it read from {@link #in}. It should be called in a loop * until it returns -1 signaling that the {@link java.io.InputStream} is * completely processed. + * <p> + * On successful completion (return -1) or on Exception, the streams + * {@link #in} and {@link #out} are closed by the implementation. * * @return the number of bytes read from the {@link java.io.InputStream} or * -1. -1 means that the {@link java.io.InputStream} is completely diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java index e2411d6043..1ad7a3055a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java @@ -303,7 +303,8 @@ public class BlameGenerator implements AutoCloseable { throws IOException { if (description == null) description = JGitText.get().blameNotCommittedYet; - BlobCandidate c = new BlobCandidate(description, resultPath); + BlobCandidate c = new BlobCandidate(getRepository(), description, + resultPath); c.sourceText = contents; c.regionList = new Region(0, 0, contents.size()); remaining = contents.size(); @@ -333,7 +334,8 @@ public class BlameGenerator implements AutoCloseable { if (ldr.getType() == OBJ_BLOB) { if (description == null) description = JGitText.get().blameNotCommittedYet; - BlobCandidate c = new BlobCandidate(description, resultPath); + BlobCandidate c = new BlobCandidate(getRepository(), description, + resultPath); c.sourceBlob = id.toObjectId(); c.sourceText = new RawText(ldr.getCachedBytes(Integer.MAX_VALUE)); c.regionList = new Region(0, 0, c.sourceText.size()); @@ -346,7 +348,7 @@ public class BlameGenerator implements AutoCloseable { if (!find(commit, resultPath)) return this; - Candidate c = new Candidate(commit, resultPath); + Candidate c = new Candidate(getRepository(), commit, resultPath); c.sourceBlob = idBuf.toObjectId(); c.loadText(reader); c.regionList = new Region(0, 0, c.sourceText.size()); @@ -430,7 +432,8 @@ public class BlameGenerator implements AutoCloseable { // just pump the queue } - ReverseCandidate c = new ReverseCandidate(result, resultPath); + ReverseCandidate c = new ReverseCandidate(getRepository(), result, + resultPath); c.sourceBlob = idBuf.toObjectId(); c.loadText(reader); c.regionList = new Region(0, 0, c.sourceText.size()); @@ -637,7 +640,8 @@ public class BlameGenerator implements AutoCloseable { return false; } - Candidate next = n.create(parent, PathFilter.create(r.getOldPath())); + Candidate next = n.create(getRepository(), parent, + PathFilter.create(r.getOldPath())); next.sourceBlob = r.getOldId().toObjectId(); next.renameScore = r.getScore(); next.loadText(reader); @@ -653,7 +657,7 @@ public class BlameGenerator implements AutoCloseable { private boolean splitBlameWithParent(Candidate n, RevCommit parent) throws IOException { - Candidate next = n.create(parent, n.sourcePath); + Candidate next = n.create(getRepository(), parent, n.sourcePath); next.sourceBlob = idBuf.toObjectId(); next.loadText(reader); return split(next, n); @@ -740,12 +744,12 @@ public class BlameGenerator implements AutoCloseable { Candidate p; if (renames != null && renames[pIdx] != null) { - p = n.create(parent, + p = n.create(getRepository(), parent, PathFilter.create(renames[pIdx].getOldPath())); p.renameScore = renames[pIdx].getScore(); p.sourceBlob = renames[pIdx].getOldId().toObjectId(); } else if (ids != null && ids[pIdx] != null) { - p = n.create(parent, n.sourcePath); + p = n.create(getRepository(), parent, n.sourcePath); p.sourceBlob = ids[pIdx]; } else { continue; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/Candidate.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/Candidate.java index 855ef78252..457d1d2cea 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/Candidate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/Candidate.java @@ -55,10 +55,12 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevFlag; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.filter.PathFilter; +import org.eclipse.jgit.util.LfsFactory; /** * A source that may have supplied some (or all) of the result file. @@ -109,7 +111,11 @@ class Candidate { */ int renameScore; - Candidate(RevCommit commit, PathFilter path) { + /** repository used for LFS blob handling */ + private Repository sourceRepository; + + Candidate(Repository repo, RevCommit commit, PathFilter path) { + sourceRepository = repo; sourceCommit = commit; sourcePath = path; } @@ -150,12 +156,12 @@ class Candidate { return sourceCommit.getAuthorIdent(); } - Candidate create(RevCommit commit, PathFilter path) { - return new Candidate(commit, path); + Candidate create(Repository repo, RevCommit commit, PathFilter path) { + return new Candidate(repo, commit, path); } Candidate copy(RevCommit commit) { - Candidate r = create(commit, sourcePath); + Candidate r = create(sourceRepository, commit, sourcePath); r.sourceBlob = sourceBlob; r.sourceText = sourceText; r.regionList = regionList; @@ -164,7 +170,11 @@ class Candidate { } void loadText(ObjectReader reader) throws IOException { - ObjectLoader ldr = reader.open(sourceBlob, Constants.OBJ_BLOB); + ObjectLoader ldr = LfsFactory.getInstance().applySmudgeFilter(sourceRepository, + reader.open(sourceBlob, Constants.OBJ_BLOB), + LfsFactory.getAttributesForPath(sourceRepository, + sourcePath.getPath(), sourceCommit) + .get(Constants.ATTR_DIFF)); sourceText = new RawText(ldr.getCachedBytes(Integer.MAX_VALUE)); } @@ -349,8 +359,9 @@ class Candidate { * children pointers, allowing reverse navigation of history. */ static final class ReverseCandidate extends Candidate { - ReverseCandidate(ReverseCommit commit, PathFilter path) { - super(commit, path); + ReverseCandidate(Repository repo, ReverseCommit commit, + PathFilter path) { + super(repo, commit, path); } @Override @@ -370,8 +381,8 @@ class Candidate { } @Override - Candidate create(RevCommit commit, PathFilter path) { - return new ReverseCandidate((ReverseCommit) commit, path); + Candidate create(Repository repo, RevCommit commit, PathFilter path) { + return new ReverseCandidate(repo, (ReverseCommit) commit, path); } @Override @@ -400,8 +411,8 @@ class Candidate { /** Author name to refer to this blob with. */ String description; - BlobCandidate(String name, PathFilter path) { - super(null, path); + BlobCandidate(Repository repo, String name, PathFilter path) { + super(repo, null, path); description = name; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java index 5ede3ea6d4..f3e986a5e7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java @@ -170,10 +170,11 @@ public abstract class ContentSource { @Override public ObjectLoader open(String path, ObjectId id) throws IOException { seek(path); + long entrySize = ptr.getEntryContentLength(); return new ObjectLoader() { @Override public long getSize() { - return ptr.getEntryLength(); + return entrySize; } @Override @@ -184,7 +185,7 @@ public abstract class ContentSource { @Override public ObjectStream openStream() throws MissingObjectException, IOException { - long contentLength = ptr.getEntryContentLength(); + long contentLength = entrySize; InputStream in = ptr.openEntryStream(); in = new BufferedInputStream(in); return new ObjectStream.Filter(getType(), contentLength, in); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java index 0f5ea76519..5c8343f92c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java @@ -48,9 +48,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.eclipse.jgit.attributes.Attribute; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AbbreviatedObjectId; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.MutableObjectId; import org.eclipse.jgit.lib.ObjectId; @@ -196,6 +198,11 @@ public class DiffEntry { entry.newMode = walk.getFileMode(1); entry.newPath = entry.oldPath = walk.getPathString(); + if (walk.getAttributesNodeProvider() != null) { + entry.diffAttribute = walk.getAttributes() + .get(Constants.ATTR_DIFF); + } + if (treeFilterMarker != null) entry.treeFilterMarks = treeFilterMarker.getMarks(walk); @@ -282,6 +289,7 @@ public class DiffEntry { del.newMode = FileMode.MISSING; del.newPath = DiffEntry.DEV_NULL; del.changeType = ChangeType.DELETE; + del.diffAttribute = entry.diffAttribute; DiffEntry add = new DiffEntry(); add.oldId = A_ZERO; @@ -292,6 +300,7 @@ public class DiffEntry { add.newMode = entry.getNewMode(); add.newPath = entry.getNewPath(); add.changeType = ChangeType.ADD; + add.diffAttribute = entry.diffAttribute; return Arrays.asList(del, add); } @@ -306,6 +315,7 @@ public class DiffEntry { r.newId = dst.newId; r.newMode = dst.newMode; r.newPath = dst.newPath; + r.diffAttribute = dst.diffAttribute; r.changeType = changeType; r.score = score; @@ -321,6 +331,13 @@ public class DiffEntry { /** File name of the new (post-image). */ protected String newPath; + /** + * diff filter attribute + * + * @since 4.11 + */ + protected Attribute diffAttribute; + /** Old mode of the file, if described by the patch, else null. */ protected FileMode oldMode; @@ -395,6 +412,14 @@ public class DiffEntry { } /** + * @return the {@link Attribute} determining filters to be applied. + * @since 4.11 + */ + public Attribute getDiffAttribute() { + return diffAttribute; + } + + /** * Get the old file mode * * @return the old file mode, if described in the patch diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java index bf9a27bdc9..2e29b81779 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java @@ -98,6 +98,7 @@ import org.eclipse.jgit.treewalk.filter.IndexDiffFilter; import org.eclipse.jgit.treewalk.filter.NotIgnoredFilter; import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter; +import org.eclipse.jgit.util.LfsFactory; import org.eclipse.jgit.util.QuotedString; /** @@ -141,6 +142,8 @@ public class DiffFormatter implements AutoCloseable { private ContentSource.Pair source; + private Repository repository; + /** * Create a new formatter with a default level of context. * @@ -172,6 +175,7 @@ public class DiffFormatter implements AutoCloseable { * source repository holding referenced objects. */ public void setRepository(Repository repository) { + this.repository = repository; setReader(repository.newObjectReader(), repository.getConfig(), true); } @@ -1057,7 +1061,8 @@ public class DiffFormatter implements AutoCloseable { throw new AmbiguousObjectException(id, ids); } - ObjectLoader ldr = source.open(side, entry); + ObjectLoader ldr = LfsFactory.getInstance().applySmudgeFilter(repository, + source.open(side, entry), entry.getDiffAttribute()); return RawText.load(ldr, binaryFileThreshold); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java index 7be1659105..ec88ce4ff7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java @@ -74,7 +74,7 @@ public class RawText extends Sequence { public static final RawText EMPTY_TEXT = new RawText(new byte[0]); /** Number of bytes to check for heuristics in {@link #isBinary(byte[])} */ - private static final int FIRST_FEW_BYTES = 8000; + static final int FIRST_FEW_BYTES = 8000; /** The file content for this sequence. */ protected final byte[] content; @@ -110,6 +110,14 @@ public class RawText extends Sequence { this(IO.readFully(file)); } + /** + * @return the raw, unprocessed content read. + * @since 4.11 + */ + public byte[] getRawContent() { + return content; + } + /** @return total number of items in the sequence. */ /** {@inheritDoc} */ @Override diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java index d41a1f57fb..0b03eb1521 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -56,6 +56,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import org.eclipse.jgit.api.errors.CanceledException; import org.eclipse.jgit.api.errors.FilterFailedException; import org.eclipse.jgit.attributes.FilterCommand; import org.eclipse.jgit.attributes.FilterCommandRegistry; @@ -66,6 +67,7 @@ import org.eclipse.jgit.errors.IndexWriteException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.events.WorkingTreeModifiedEvent; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; import org.eclipse.jgit.lib.CoreConfig.EolStreamType; @@ -76,6 +78,7 @@ import org.eclipse.jgit.lib.ObjectChecker; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.treewalk.AbstractTreeIterator; import org.eclipse.jgit.treewalk.CanonicalTreeParser; @@ -158,6 +161,8 @@ public class DirCacheCheckout { private boolean performingCheckout; + private ProgressMonitor monitor = NullProgressMonitor.INSTANCE; + /** * Get list of updated paths and smudgeFilterCommands * @@ -288,6 +293,18 @@ public class DirCacheCheckout { } /** + * Set a progress monitor which can be passed to built-in filter commands, + * providing progress information for long running tasks. + * + * @param monitor + * the {@link ProgressMonitor} + * @since 4.11 + */ + public void setProgressMonitor(ProgressMonitor monitor) { + this.monitor = monitor != null ? monitor : NullProgressMonitor.INSTANCE; + } + + /** * Scan head, index and merge tree. Used during normal checkout or merge * operations. * @@ -465,6 +482,10 @@ public class DirCacheCheckout { public boolean checkout() throws IOException { try { return doCheckout(); + } catch (CanceledException ce) { + // should actually be propagated, but this would change a LOT of + // APIs + throw new IOException(ce); } finally { try { dc.unlock(); @@ -482,7 +503,7 @@ public class DirCacheCheckout { private boolean doCheckout() throws CorruptObjectException, IOException, MissingObjectException, IncorrectObjectTypeException, - CheckoutConflictException, IndexWriteException { + CheckoutConflictException, IndexWriteException, CanceledException { toBeDeleted.clear(); try (ObjectReader objectReader = repo.getObjectDatabase().newReader()) { if (headCommitTree != null) @@ -500,6 +521,10 @@ public class DirCacheCheckout { // update our index builder.finish(); + // init progress reporting + int numTotal = removed.size() + updated.size(); + monitor.beginTask(JGitText.get().checkingOutFiles, numTotal); + performingCheckout = true; File file = null; String last = null; @@ -525,6 +550,12 @@ public class DirCacheCheckout { removeEmptyParents(new File(repo.getWorkTree(), last)); last = r; } + monitor.update(1); + if (monitor.isCancelled()) { + throw new CanceledException(MessageFormat.format( + JGitText.get().operationCanceled, + JGitText.get().checkingOutFiles)); + } } if (file != null) { removeEmptyParents(file); @@ -540,10 +571,19 @@ public class DirCacheCheckout { String path = e.getKey(); CheckoutMetadata meta = e.getValue(); DirCacheEntry entry = dc.getEntry(path); - if (!FileMode.GITLINK.equals(entry.getRawMode())) { + if (FileMode.GITLINK.equals(entry.getRawMode())) { + checkoutGitlink(path, entry); + } else { checkoutEntry(repo, entry, objectReader, false, meta); } e = null; + + monitor.update(1); + if (monitor.isCancelled()) { + throw new CanceledException(MessageFormat.format( + JGitText.get().operationCanceled, + JGitText.get().checkingOutFiles)); + } } } catch (Exception ex) { // We didn't actually modify the current entry nor any that @@ -557,6 +597,8 @@ public class DirCacheCheckout { } throw ex; } + monitor.endTask(); + // commit the index builder - a new index is persisted if (!builder.commit()) throw new IndexWriteException(); @@ -564,6 +606,14 @@ public class DirCacheCheckout { return toBeDeleted.size() == 0; } + private void checkoutGitlink(String path, DirCacheEntry entry) + throws IOException { + File gitlinkDir = new File(repo.getWorkTree(), path); + FileUtils.mkdirs(gitlinkDir, true); + FS fs = repo.getFS(); + entry.setLastModified(fs.lastModified(gitlinkDir)); + } + private static ArrayList<String> filterOut(ArrayList<String> strings, IntList indicesToRemove) { int n = indicesToRemove.size(); @@ -1490,6 +1540,10 @@ public class DirCacheCheckout { private static void runBuiltinFilterCommand(Repository repo, CheckoutMetadata checkoutMetadata, ObjectLoader ol, OutputStream channel) throws MissingObjectException, IOException { + boolean isMandatory = repo.getConfig().getBoolean( + ConfigConstants.CONFIG_FILTER_SECTION, + ConfigConstants.CONFIG_SECTION_LFS, + ConfigConstants.CONFIG_KEY_REQUIRED, false); FilterCommand command = null; try { command = FilterCommandRegistry.createFilterCommand( @@ -1497,9 +1551,14 @@ public class DirCacheCheckout { channel); } catch (IOException e) { LOG.error(JGitText.get().failedToDetermineFilterDefinition, e); - // In case an IOException occurred during creating of the command - // then proceed as if there would not have been a builtin filter. - ol.copyTo(channel); + if (!isMandatory) { + // In case an IOException occurred during creating of the + // command then proceed as if there would not have been a + // builtin filter (only if the filter is not mandatory). + ol.copyTo(channel); + } else { + throw e; + } } if (command != null) { while (command.run() != -1) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java index 68521d3981..d768216db6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java @@ -329,11 +329,8 @@ public class DirCacheIterator extends AbstractTreeIterator { AttributesNode r = new AttributesNode(); ObjectLoader loader = reader.open(objectId); if (loader != null) { - InputStream in = loader.openStream(); - try { + try (InputStream in = loader.openStream()) { r.parse(in); - } finally { - in.close(); } } return r.getRules().isEmpty() ? null : r; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java index c7bc1b6aa3..8b8df87978 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.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.StringJoiner; @@ -124,7 +125,6 @@ public class RepoCommand extends GitCommand<RevCommit> { private boolean ignoreRemoteFailures = false; private List<RepoProject> bareProjects; - private Git git; private ProgressMonitor monitor; /** @@ -486,58 +486,50 @@ public class RepoCommand extends GitCommand<RevCommit> { /** {@inheritDoc} */ @Override public RevCommit call() throws GitAPIException { - try { - checkCallable(); - if (baseUri == null) { - baseUri = ""; //$NON-NLS-1$ - } - if (inputStream == null) { - if (manifestPath == null || manifestPath.length() == 0) - throw new IllegalArgumentException( - JGitText.get().pathNotConfigured); - try { - inputStream = new FileInputStream(manifestPath); - } catch (IOException e) { - throw new IllegalArgumentException( - JGitText.get().pathNotConfigured); - } - } - - if (repo.isBare()) { - bareProjects = new ArrayList<>(); - if (author == null) - author = new PersonIdent(repo); - if (callback == null) - callback = new DefaultRemoteReader(); - } else - git = new Git(repo); - - ManifestParser parser = new ManifestParser( - includedReader, manifestPath, branch, baseUri, groupsParam, repo); + checkCallable(); + if (baseUri == null) { + baseUri = ""; //$NON-NLS-1$ + } + if (inputStream == null) { + if (manifestPath == null || manifestPath.length() == 0) + throw new IllegalArgumentException( + JGitText.get().pathNotConfigured); try { - parser.read(inputStream); - for (RepoProject proj : parser.getFilteredProjects()) { - addSubmodule(proj.getUrl(), - proj.getPath(), - proj.getRevision(), - proj.getCopyFiles(), - proj.getLinkFiles(), - proj.getGroups(), - proj.getRecommendShallow()); - } - } catch (GitAPIException | IOException e) { - throw new ManifestErrorException(e); + inputStream = new FileInputStream(manifestPath); + } catch (IOException e) { + throw new IllegalArgumentException( + JGitText.get().pathNotConfigured); } + } + + List<RepoProject> filteredProjects; + try { + ManifestParser parser = new ManifestParser(includedReader, + manifestPath, branch, baseUri, groupsParam, repo); + parser.read(inputStream); + filteredProjects = parser.getFilteredProjects(); + } catch (IOException e) { + throw new ManifestErrorException(e); } finally { try { - if (inputStream != null) - inputStream.close(); + inputStream.close(); } catch (IOException e) { // Just ignore it, it's not important. } } if (repo.isBare()) { + bareProjects = new ArrayList<>(); + if (author == null) + author = new PersonIdent(repo); + if (callback == null) + callback = new DefaultRemoteReader(); + for (RepoProject proj : filteredProjects) { + addSubmoduleBare(proj.getUrl(), proj.getPath(), + proj.getRevision(), proj.getCopyFiles(), + proj.getLinkFiles(), proj.getGroups(), + proj.getRecommendShallow()); + } DirCache index = DirCache.newInCore(); DirCacheBuilder builder = index.builder(); ObjectInserter inserter = repo.newObjectInserter(); @@ -552,10 +544,7 @@ public class RepoCommand extends GitCommand<RevCommit> { objectId = ObjectId.fromString(proj.getRevision()); } else { objectId = callback.sha1(nameUri, proj.getRevision()); - if (objectId == null) { - if (ignoreRemoteFailures) { - continue; - } + if (objectId == null && !ignoreRemoteFailures) { throw new RemoteUnavailableException(nameUri); } if (recordRemoteBranch) { @@ -594,38 +583,40 @@ public class RepoCommand extends GitCommand<RevCommit> { cfg.setString("submodule", path, "url", submodUrl.toString()); //$NON-NLS-1$ //$NON-NLS-2$ // create gitlink - DirCacheEntry dcEntry = new DirCacheEntry(path); - dcEntry.setObjectId(objectId); - dcEntry.setFileMode(FileMode.GITLINK); - builder.add(dcEntry); - - for (CopyFile copyfile : proj.getCopyFiles()) { - byte[] src = callback.readFile( - nameUri, proj.getRevision(), copyfile.src); - objectId = inserter.insert(Constants.OBJ_BLOB, src); - dcEntry = new DirCacheEntry(copyfile.dest); + if (objectId != null) { + DirCacheEntry dcEntry = new DirCacheEntry(path); dcEntry.setObjectId(objectId); - dcEntry.setFileMode(FileMode.REGULAR_FILE); + dcEntry.setFileMode(FileMode.GITLINK); builder.add(dcEntry); - } - for (LinkFile linkfile : proj.getLinkFiles()) { - String link; - if (linkfile.dest.contains("/")) { //$NON-NLS-1$ - link = FileUtils.relativizeGitPath( + + for (CopyFile copyfile : proj.getCopyFiles()) { + byte[] src = callback.readFile( + nameUri, proj.getRevision(), copyfile.src); + objectId = inserter.insert(Constants.OBJ_BLOB, src); + dcEntry = new DirCacheEntry(copyfile.dest); + dcEntry.setObjectId(objectId); + dcEntry.setFileMode(FileMode.REGULAR_FILE); + builder.add(dcEntry); + } + for (LinkFile linkfile : proj.getLinkFiles()) { + String link; + if (linkfile.dest.contains("/")) { //$NON-NLS-1$ + link = FileUtils.relativizeGitPath( linkfile.dest.substring(0, - linkfile.dest.lastIndexOf('/')), + linkfile.dest.lastIndexOf('/')), proj.getPath() + "/" + linkfile.src); //$NON-NLS-1$ - } else { - link = proj.getPath() + "/" + linkfile.src; //$NON-NLS-1$ - } + } else { + link = proj.getPath() + "/" + linkfile.src; //$NON-NLS-1$ + } - objectId = inserter.insert(Constants.OBJ_BLOB, + objectId = inserter.insert(Constants.OBJ_BLOB, link.getBytes( - Constants.CHARACTER_ENCODING)); - dcEntry = new DirCacheEntry(linkfile.dest); - dcEntry.setObjectId(objectId); - dcEntry.setFileMode(FileMode.SYMLINK); - builder.add(dcEntry); + Constants.CHARACTER_ENCODING)); + dcEntry = new DirCacheEntry(linkfile.dest); + dcEntry.setObjectId(objectId); + dcEntry.setFileMode(FileMode.SYMLINK); + builder.add(dcEntry); + } } } String content = cfg.toText(); @@ -653,6 +644,11 @@ public class RepoCommand extends GitCommand<RevCommit> { // Create a Commit object, populate it and write it ObjectId headId = repo.resolve(targetBranch + "^{commit}"); //$NON-NLS-1$ + if (headId != null && rw.parseCommit(headId).getTree().getId().equals(treeId)) { + // No change. Do nothing. + return rw.parseCommit(headId); + } + CommitBuilder commit = new CommitBuilder(); commit.setTreeId(treeId); if (headId != null) @@ -689,55 +685,64 @@ public class RepoCommand extends GitCommand<RevCommit> { } return rw.parseCommit(commitId); - } catch (IOException e) { + } catch (GitAPIException | IOException e) { throw new ManifestErrorException(e); } } else { - return git - .commit() - .setMessage(RepoText.get().repoCommitMessage) - .call(); + try (Git git = new Git(repo)) { + for (RepoProject proj : filteredProjects) { + addSubmodule(proj.getUrl(), proj.getPath(), + proj.getRevision(), proj.getCopyFiles(), + proj.getLinkFiles(), git); + } + return git.commit().setMessage(RepoText.get().repoCommitMessage) + .call(); + } catch (GitAPIException | IOException e) { + throw new ManifestErrorException(e); + } } } private void addSubmodule(String url, String path, String revision, - List<CopyFile> copyfiles, List<LinkFile> linkfiles, - Set<String> groups, String recommendShallow) + List<CopyFile> copyfiles, List<LinkFile> linkfiles, Git git) throws GitAPIException, IOException { - if (repo.isBare()) { - RepoProject proj = new RepoProject(url, path, revision, null, groups, recommendShallow); - proj.addCopyFiles(copyfiles); - proj.addLinkFiles(linkfiles); - bareProjects.add(proj); - } else { - if (!linkfiles.isEmpty()) { - throw new UnsupportedOperationException( - JGitText.get().nonBareLinkFilesNotSupported); - } + assert (!repo.isBare()); + assert (git != null); + if (!linkfiles.isEmpty()) { + throw new UnsupportedOperationException( + JGitText.get().nonBareLinkFilesNotSupported); + } - SubmoduleAddCommand add = git - .submoduleAdd() - .setPath(path) - .setURI(url); - if (monitor != null) - add.setProgressMonitor(monitor); - - Repository subRepo = add.call(); - if (revision != null) { - try (Git sub = new Git(subRepo)) { - sub.checkout().setName(findRef(revision, subRepo)) - .call(); - } - subRepo.close(); - git.add().addFilepattern(path).call(); - } - for (CopyFile copyfile : copyfiles) { - copyfile.copy(); - git.add().addFilepattern(copyfile.dest).call(); + SubmoduleAddCommand add = git.submoduleAdd().setPath(path).setURI(url); + if (monitor != null) + add.setProgressMonitor(monitor); + + Repository subRepo = add.call(); + if (revision != null) { + try (Git sub = new Git(subRepo)) { + sub.checkout().setName(findRef(revision, subRepo)).call(); } + subRepo.close(); + git.add().addFilepattern(path).call(); + } + for (CopyFile copyfile : copyfiles) { + copyfile.copy(); + git.add().addFilepattern(copyfile.dest).call(); } } + private void addSubmoduleBare(String url, String path, String revision, + List<CopyFile> copyfiles, List<LinkFile> linkfiles, + Set<String> groups, String recommendShallow) { + assert (repo.isBare()); + assert (bareProjects != null); + RepoProject proj = new RepoProject(url, path, revision, null, groups, + recommendShallow); + proj.addCopyFiles(copyfiles); + proj.addLinkFiles(linkfiles); + bareProjects.add(proj); + } + /* * Assume we are document "a/b/index.html", what should we put in a href to get to "a/" ? * Returns the child if either base or child is not a bare path. This provides a missing feature in @@ -745,12 +750,7 @@ public class RepoCommand extends GitCommand<RevCommit> { */ private static final String SLASH = "/"; //$NON-NLS-1$ static URI relativize(URI current, URI target) { - - // We only handle bare paths for now. - if (!target.toString().equals(target.getPath())) { - return target; - } - if (!current.toString().equals(current.getPath())) { + if (!Objects.equals(current.getHost(), target.getHost())) { return target; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java index fb015c9166..ad43e2ca83 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java @@ -172,4 +172,15 @@ abstract class GitHook<T> implements Callable<T> { } } + /** + * Check whether a 'native' (i.e. script) hook is installed in the + * repository. + * + * @return whether a native hook script is installed in the repository. + * @since 4.11 + */ + public boolean isNativeHookPresent() { + return FS.DETECTED.findHook(getRepository(), getHookName()) != null; + } + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java index 87db36b251..79395ed23f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java @@ -43,8 +43,11 @@ package org.eclipse.jgit.hooks; import java.io.PrintStream; +import java.text.MessageFormat; +import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.util.LfsFactory; /** * Factory class for instantiating supported hooks. @@ -107,6 +110,17 @@ public class Hooks { * @since 4.2 */ public static PrePushHook prePush(Repository repo, PrintStream outputStream) { + if (LfsFactory.getInstance().isAvailable()) { + PrePushHook hook = LfsFactory.getInstance().getPrePushHook(repo, + outputStream); + if (hook != null) { + if (hook.isNativeHookPresent()) { + throw new IllegalStateException(MessageFormat + .format(JGitText.get().lfsHookConflict, repo)); + } + return hook; + } + } return new PrePushHook(repo, outputStream); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java index f974eb7927..051a1d1c81 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java @@ -137,6 +137,16 @@ public class PrePushHook extends GitHook<String> { } /** + * Get remote name + * + * @return remote name or null + * @since 4.11 + */ + protected String getRemoteName() { + return remoteName; + } + + /** * Set remote location * * @param location diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java index 460f0ed4b1..31e173bf23 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java @@ -152,11 +152,35 @@ public class FastIgnoreRule { * result. */ public boolean isMatch(String path, boolean directory) { + return isMatch(path, directory, false); + } + + /** + * Returns true if a match was made. <br> + * This function does NOT return the actual ignore status of the target! + * Please consult {@link #getResult()} for the negation status. The actual + * ignore status may be true or false depending on whether this rule is an + * ignore rule or a negation rule. + * + * @param path + * Name pattern of the file, relative to the base directory of + * this rule + * @param directory + * Whether the target file is a directory or not + * @param pathMatch + * {@code true} if the match is for the full path: see + * {@link IMatcher#matches(String, int, int)} + * @return True if a match was made. This does not necessarily mean that the + * target is ignored. Call {@link #getResult() getResult()} for the + * result. + * @since 4.11 + */ + public boolean isMatch(String path, boolean directory, boolean pathMatch) { if (path == null) return false; if (path.length() == 0) return false; - boolean match = matcher.matches(path, directory, false); + boolean match = matcher.matches(path, directory, pathMatch); return match; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java index 1ad60101e7..1224df4226 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java @@ -145,7 +145,13 @@ public class IgnoreNode { * @return status of the path. */ public MatchResult isIgnored(String entryPath, boolean isDirectory) { - return isIgnored(entryPath, isDirectory, false); + final Boolean result = checkIgnored(entryPath, isDirectory); + if (result == null) { + return MatchResult.CHECK_PARENT; + } + + return result.booleanValue() ? MatchResult.IGNORED + : MatchResult.NOT_IGNORED; } /** @@ -159,45 +165,48 @@ public class IgnoreNode { * true if the target item is a directory. * @param negateFirstMatch * true if the first match should be negated + * @deprecated negateFirstMatch is not honored anymore * @return status of the path. * @since 3.6 */ + @Deprecated public MatchResult isIgnored(String entryPath, boolean isDirectory, boolean negateFirstMatch) { - if (rules.isEmpty()) - if (negateFirstMatch) - return MatchResult.CHECK_PARENT_NEGATE_FIRST_MATCH; - else - return MatchResult.CHECK_PARENT; + final Boolean result = checkIgnored(entryPath, isDirectory); + if (result == null) { + return negateFirstMatch + ? MatchResult.CHECK_PARENT_NEGATE_FIRST_MATCH + : MatchResult.CHECK_PARENT; + } + + return result.booleanValue() ? MatchResult.IGNORED + : MatchResult.NOT_IGNORED; + } - // Parse rules in the reverse order that they were read + /** + * Determine if an entry path matches an ignore rule. + * + * @param entryPath + * the path to test. The path must be relative to this ignore + * node's own repository path, and in repository path format + * (uses '/' and not '\'). + * @param isDirectory + * true if the target item is a directory. + * @return Boolean.TRUE, if the entry is ignored; Boolean.FALSE, if the + * entry is forced to be not ignored (negated match); or null, if + * undetermined + * @since 4.11 + */ + public Boolean checkIgnored(String entryPath, boolean isDirectory) { + // Parse rules in the reverse order that they were read because later + // rules have higher priority for (int i = rules.size() - 1; i > -1; i--) { FastIgnoreRule rule = rules.get(i); - if (rule.isMatch(entryPath, isDirectory)) { - if (rule.getResult()) { - // rule matches: path could be ignored - if (negateFirstMatch) - // ignore current match, reset "negate" flag, continue - negateFirstMatch = false; - else - // valid match, just return - return MatchResult.IGNORED; - } else { - // found negated rule - if (negateFirstMatch) - // not possible to re-include excluded ignore rule - return MatchResult.NOT_IGNORED; - else - // set the flag and continue - negateFirstMatch = true; - } + if (rule.isMatch(entryPath, isDirectory, true)) { + return Boolean.valueOf(rule.getResult()); } } - if (negateFirstMatch) - // negated rule found but there is no previous rule in *this* file - return MatchResult.CHECK_PARENT_NEGATE_FIRST_MATCH; - // *this* file has no matching rules - return MatchResult.CHECK_PARENT; + return null; } /** {@inheritDoc} */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java index dbf06385c5..14440d253d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java @@ -58,8 +58,7 @@ public interface IMatcher { } @Override - public boolean matches(String segment, int startIncl, int endExcl, - boolean assumeDirectory) { + public boolean matches(String segment, int startIncl, int endExcl) { return false; } }; @@ -91,11 +90,7 @@ public interface IMatcher { * start index, inclusive * @param endExcl * end index, exclusive - * @param assumeDirectory - * true to assume this path as directory (even if it doesn't end - * with a slash) * @return true if this matcher pattern matches given string */ - boolean matches(String segment, int startIncl, int endExcl, - boolean assumeDirectory); + boolean matches(String segment, int startIncl, int endExcl); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java index 2bae8f229f..fdd39bcc79 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java @@ -57,8 +57,7 @@ public class LeadingAsteriskMatcher extends NameMatcher { /** {@inheritDoc} */ @Override - public boolean matches(String segment, int startIncl, int endExcl, - boolean assumeDirectory) { + public boolean matches(String segment, int startIncl, int endExcl) { // faster local access, same as in string.indexOf() String s = subPattern; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java index 6a4b2b8677..33f0fe71d2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java @@ -91,12 +91,12 @@ public class NameMatcher extends AbstractMatcher { } boolean match; if (lastSlash < start) { - match = matches(path, start, stop, assumeDirectory); + match = matches(path, start, stop); } else { // Can't match if the path contains a slash if the pattern is // anchored at the beginning match = !beginning - && matches(path, lastSlash + 1, stop, assumeDirectory); + && matches(path, lastSlash + 1, stop); } if (match && dirOnly) { match = assumeDirectory; @@ -108,7 +108,7 @@ public class NameMatcher extends AbstractMatcher { if (end < 0) { end = stop; } - if (end > start && matches(path, start, end, assumeDirectory)) { + if (end > start && matches(path, start, end)) { // make sure the directory matches: either if we are done with // segment and there is next one, or if the directory is assumed return !dirOnly || assumeDirectory || end < stop; @@ -123,8 +123,7 @@ public class NameMatcher extends AbstractMatcher { /** {@inheritDoc} */ @Override - public boolean matches(String segment, int startIncl, int endExcl, - boolean assumeDirectory) { + public boolean matches(String segment, int startIncl, int endExcl) { // faster local access, same as in string.indexOf() String s = subPattern; int length = s.length(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java index 50e78ae751..3c0f17ab3d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java @@ -61,7 +61,10 @@ import org.eclipse.jgit.ignore.internal.Strings.PatternState; */ public class PathMatcher extends AbstractMatcher { - private static final WildMatcher WILD = WildMatcher.INSTANCE; + private static final WildMatcher WILD_NO_DIRECTORY = new WildMatcher(false); + + private static final WildMatcher WILD_ONLY_DIRECTORY = new WildMatcher( + true); private final List<IMatcher> matchers; @@ -94,11 +97,15 @@ public class PathMatcher extends AbstractMatcher { for (int i = 0; i < segments.size(); i++) { String segment = segments.get(i); IMatcher matcher = createNameMatcher0(segment, pathSeparator, - dirOnly); - if (matcher == WILD && i > 0 - && matchers.get(matchers.size() - 1) == WILD) - // collapse wildmatchers **/** is same as ** - continue; + dirOnly, i == segments.size() - 1); + if (i > 0) { + final IMatcher last = matchers.get(matchers.size() - 1); + if (isWild(matcher) && isWild(last)) + // collapse wildmatchers **/** is same as **, but preserve + // dirOnly flag (i.e. always use the last wildmatcher) + matchers.remove(matchers.size() - 1); + } + matchers.add(matcher); } return matchers; @@ -126,7 +133,7 @@ public class PathMatcher extends AbstractMatcher { int slashIdx = pattern.indexOf(slash, 1); if (slashIdx > 0 && slashIdx < pattern.length() - 1) return new PathMatcher(pattern, pathSeparator, dirOnly); - return createNameMatcher0(pattern, pathSeparator, dirOnly); + return createNameMatcher0(pattern, pathSeparator, dirOnly, true); } /** @@ -153,12 +160,13 @@ public class PathMatcher extends AbstractMatcher { } private static IMatcher createNameMatcher0(String segment, - Character pathSeparator, boolean dirOnly) + Character pathSeparator, boolean dirOnly, boolean lastSegment) throws InvalidPatternException { // check if we see /** or ** segments => double star pattern if (WildMatcher.WILDMATCH.equals(segment) || WildMatcher.WILDMATCH2.equals(segment)) - return WILD; + return dirOnly && lastSegment ? WILD_ONLY_DIRECTORY + : WILD_NO_DIRECTORY; PatternState state = checkWildCards(segment); switch (state) { @@ -218,8 +226,7 @@ public class PathMatcher extends AbstractMatcher { /** {@inheritDoc} */ @Override - public boolean matches(String segment, int startIncl, int endExcl, - boolean assumeDirectory) { + public boolean matches(String segment, int startIncl, int endExcl) { throw new UnsupportedOperationException( "Path matcher works only on entire paths"); //$NON-NLS-1$ } @@ -241,18 +248,18 @@ public class PathMatcher extends AbstractMatcher { if (right == -1) { if (left < endExcl) { match = matches(matcher, path, left, endExcl, - assumeDirectory); + assumeDirectory, pathMatch); } else { // a/** should not match a/ or a - match = match && matchers.get(matcher) != WILD; + match = match && !isWild(matchers.get(matcher)); } if (match) { if (matcher < matchers.size() - 1 - && matchers.get(matcher) == WILD) { + && isWild(matchers.get(matcher))) { // ** can match *nothing*: a/**/b match also a/b matcher++; match = matches(matcher, path, left, endExcl, - assumeDirectory); + assumeDirectory, pathMatch); } else if (dirOnly && !assumeDirectory) { // Directory expectations not met return false; @@ -264,14 +271,15 @@ public class PathMatcher extends AbstractMatcher { wildmatchBacktrackPos = right; } if (right - left > 0) { - match = matches(matcher, path, left, right, assumeDirectory); + match = matches(matcher, path, left, right, assumeDirectory, + pathMatch); } else { // path starts with slash??? right++; continue; } if (match) { - boolean wasWild = matchers.get(matcher) == WILD; + boolean wasWild = isWild(matchers.get(matcher)); if (wasWild) { lastWildmatch = matcher; wildmatchBacktrackPos = -1; @@ -317,9 +325,19 @@ public class PathMatcher extends AbstractMatcher { } private boolean matches(int matcherIdx, String path, int startIncl, - int endExcl, - boolean assumeDirectory) { + int endExcl, boolean assumeDirectory, boolean pathMatch) { IMatcher matcher = matchers.get(matcherIdx); - return matcher.matches(path, startIncl, endExcl, assumeDirectory); + + final boolean matches = matcher.matches(path, startIncl, endExcl); + if (!matches || !pathMatch || matcherIdx < matchers.size() - 1 + || !(matcher instanceof AbstractMatcher)) { + return matches; + } + + return assumeDirectory || !((AbstractMatcher) matcher).dirOnly; + } + + private static boolean isWild(IMatcher matcher) { + return matcher == WILD_NO_DIRECTORY || matcher == WILD_ONLY_DIRECTORY; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java index 7df42b76cf..1e0335cef8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java @@ -57,8 +57,7 @@ public class TrailingAsteriskMatcher extends NameMatcher { /** {@inheritDoc} */ @Override - public boolean matches(String segment, int startIncl, int endExcl, - boolean assumeDirectory) { + public boolean matches(String segment, int startIncl, int endExcl) { // faster local access, same as in string.indexOf() String s = subPattern; // we don't need to count '*' character itself diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java index ca8c581d4b..3bbac8189f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java @@ -66,8 +66,7 @@ public class WildCardMatcher extends NameMatcher { /** {@inheritDoc} */ @Override - public boolean matches(String segment, int startIncl, int endExcl, - boolean assumeDirectory) { + public boolean matches(String segment, int startIncl, int endExcl) { return p.matcher(segment.substring(startIncl, endExcl)).matches(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java index ba8015cc09..2ad87dade6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java @@ -55,24 +55,26 @@ public final class WildMatcher extends AbstractMatcher { // double star for the beginning of pattern static final String WILDMATCH2 = "/**"; //$NON-NLS-1$ - static final WildMatcher INSTANCE = new WildMatcher(); - - private WildMatcher() { - super(WILDMATCH, false); + WildMatcher(boolean dirOnly) { + super(WILDMATCH, dirOnly); } /** {@inheritDoc} */ @Override public final boolean matches(String path, boolean assumeDirectory, boolean pathMatch) { - return true; + return !dirOnly || assumeDirectory + || !pathMatch && isSubdirectory(path); } /** {@inheritDoc} */ @Override - public final boolean matches(String segment, int startIncl, int endExcl, - boolean assumeDirectory) { + public final boolean matches(String segment, int startIncl, int endExcl) { return true; } + private static boolean isSubdirectory(String path) { + final int slashIndex = path.indexOf('/'); + return slashIndex >= 0 && slashIndex < path.length() - 1; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index 0b7a4c866a..4e947f8f01 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -102,6 +102,8 @@ public class JGitText extends TranslationBundle { /***/ public String blameNotCommittedYet; /***/ public String blobNotFound; /***/ public String blobNotFoundForPath; + /***/ public String blockLimitNotMultipleOfBlockSize; + /***/ public String blockLimitNotPositive; /***/ public String blockSizeNotPowerOf2; /***/ public String bothRefTargetsMustNotBeNull; /***/ public String branchNameInvalid; @@ -177,6 +179,7 @@ public class JGitText extends TranslationBundle { /***/ public String cantPassMeATree; /***/ public String channelMustBeInRange1_255; /***/ public String characterClassIsNotSupported; + /***/ public String checkingOutFiles; /***/ public String checkoutConflictWithFile; /***/ public String checkoutConflictWithFiles; /***/ public String checkoutUnexpectedResult; @@ -428,6 +431,7 @@ public class JGitText extends TranslationBundle { /***/ public String invalidIntegerValue; /***/ public String invalidKey; /***/ public String invalidLineInConfigFile; + /***/ public String invalidLineInConfigFileWithParam; /***/ public String invalidModeFor; /***/ public String invalidModeForPath; /***/ public String invalidObject; @@ -466,6 +470,7 @@ public class JGitText extends TranslationBundle { /***/ public String largeObjectException; /***/ public String largeObjectOutOfMemory; /***/ public String lengthExceedsMaximumArraySize; + /***/ public String lfsHookConflict; /***/ public String listingAlternates; /***/ public String listingPacks; /***/ public String localObjectsIncomplete; @@ -520,6 +525,7 @@ public class JGitText extends TranslationBundle { /***/ public String noMergeBase; /***/ public String noMergeHeadSpecified; /***/ public String nonBareLinkFilesNotSupported; + /***/ public String noPathAttributesFound; /***/ public String noSuchRef; /***/ public String noSuchSubmodule; /***/ public String notABoolean; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java index e558a81c09..cd7901b357 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java @@ -95,13 +95,23 @@ public class DfsBlockCacheConfig { /** * Set maximum number bytes of heap memory to dedicate to caching pack file * data. + * <p> + * It is strongly recommended to set the block limit to be an integer multiple + * of the block size. This constraint is not enforced by this method (since + * it may be called before {@link #setBlockSize(int)}), but it is enforced by + * {@link #fromConfig(Config)}. * * @param newLimit * maximum number bytes of heap memory to dedicate to caching - * pack file data. + * pack file data; must be positive. * @return {@code this} */ public DfsBlockCacheConfig setBlockLimit(final long newLimit) { + if (newLimit <= 0) { + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().blockLimitNotPositive, + Long.valueOf(newLimit))); + } blockLimit = newLimit; return this; } @@ -188,23 +198,34 @@ public class DfsBlockCacheConfig { * <p> * If a property is not defined in the configuration, then it is left * unmodified. + * <p> + * Enforces certain constraints on the combination of settings in the config, + * for example that the block limit is a multiple of the block size. * * @param rc * configuration to read properties from. * @return {@code this} */ public DfsBlockCacheConfig fromConfig(final Config rc) { - setBlockLimit(rc.getLong( + long cfgBlockLimit = rc.getLong( CONFIG_CORE_SECTION, CONFIG_DFS_SECTION, CONFIG_KEY_BLOCK_LIMIT, - getBlockLimit())); - - setBlockSize(rc.getInt( + getBlockLimit()); + int cfgBlockSize = rc.getInt( CONFIG_CORE_SECTION, CONFIG_DFS_SECTION, CONFIG_KEY_BLOCK_SIZE, - getBlockSize())); + getBlockSize()); + if (cfgBlockLimit % cfgBlockSize != 0) { + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().blockLimitNotMultipleOfBlockSize, + Long.valueOf(cfgBlockLimit), + Long.valueOf(cfgBlockSize))); + } + + setBlockLimit(cfgBlockLimit); + setBlockSize(cfgBlockSize); setConcurrencyLevel(rc.getInt( CONFIG_CORE_SECTION, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java index 27a7992c9e..197114bd6b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java @@ -44,12 +44,12 @@ package org.eclipse.jgit.internal.storage.dfs; +import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE; import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK; import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -578,10 +578,22 @@ public class DfsReader extends ObjectReader implements ObjectReuseAsIs { public void selectObjectRepresentation(PackWriter packer, ProgressMonitor monitor, Iterable<ObjectToPack> objects) throws IOException, MissingObjectException { - // Don't check dirty bit on PackList; assume ObjectToPacks all came from the - // current list. - for (DfsPackFile pack : sortPacksForSelectRepresentation()) { - List<DfsObjectToPack> tmp = findAllFromPack(pack, objects); + // Don't check dirty bit on PackList; assume ObjectToPacks all came + // from the current list. + List<DfsPackFile> packs = sortPacksForSelectRepresentation(); + trySelectRepresentation(packer, monitor, objects, packs, false); + + List<DfsPackFile> garbage = garbagePacksForSelectRepresentation(); + if (!garbage.isEmpty() && checkGarbagePacks(objects)) { + trySelectRepresentation(packer, monitor, objects, garbage, true); + } + } + + private void trySelectRepresentation(PackWriter packer, + ProgressMonitor monitor, Iterable<ObjectToPack> objects, + List<DfsPackFile> packs, boolean skipFound) throws IOException { + for (DfsPackFile pack : packs) { + List<DfsObjectToPack> tmp = findAllFromPack(pack, objects, skipFound); if (tmp.isEmpty()) continue; Collections.sort(tmp, OFFSET_SORT); @@ -620,24 +632,54 @@ public class DfsReader extends ObjectReader implements ObjectReuseAsIs { } }; - private DfsPackFile[] sortPacksForSelectRepresentation() + private List<DfsPackFile> sortPacksForSelectRepresentation() throws IOException { DfsPackFile[] packs = db.getPacks(); - DfsPackFile[] sorted = new DfsPackFile[packs.length]; - System.arraycopy(packs, 0, sorted, 0, packs.length); - Arrays.sort(sorted, PACK_SORT_FOR_REUSE); + List<DfsPackFile> sorted = new ArrayList<>(packs.length); + for (DfsPackFile p : packs) { + if (p.getPackDescription().getPackSource() != UNREACHABLE_GARBAGE) { + sorted.add(p); + } + } + Collections.sort(sorted, PACK_SORT_FOR_REUSE); return sorted; } + private List<DfsPackFile> garbagePacksForSelectRepresentation() + throws IOException { + DfsPackFile[] packs = db.getPacks(); + List<DfsPackFile> garbage = new ArrayList<>(packs.length); + for (DfsPackFile p : packs) { + if (p.getPackDescription().getPackSource() == UNREACHABLE_GARBAGE) { + garbage.add(p); + } + } + return garbage; + } + + private static boolean checkGarbagePacks(Iterable<ObjectToPack> objects) { + for (ObjectToPack otp : objects) { + if (!((DfsObjectToPack) otp).isFound()) { + return true; + } + } + return false; + } + private List<DfsObjectToPack> findAllFromPack(DfsPackFile pack, - Iterable<ObjectToPack> objects) throws IOException { + Iterable<ObjectToPack> objects, boolean skipFound) + throws IOException { List<DfsObjectToPack> tmp = new BlockList<>(); PackIndex idx = pack.getPackIndex(this); - for (ObjectToPack otp : objects) { + for (ObjectToPack obj : objects) { + DfsObjectToPack otp = (DfsObjectToPack) obj; + if (skipFound && otp.isFound()) { + continue; + } long p = idx.findOffset(otp); if (0 < p && !pack.isCorrupt(p)) { otp.setOffset(p); - tmp.add((DfsObjectToPack) otp); + tmp.add(otp); } } return tmp; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java index cf925c914d..c35801f3b0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java @@ -67,10 +67,16 @@ public class DfsReaderIoStats { /** Total number of block cache hits. */ long blockCacheHit; - /** Total number of discrete blocks read from pack file(s). */ + /** + * Total number of discrete blocks actually read from pack file(s), that is, + * block cache misses. + */ long readBlock; - /** Total number of compressed bytes read as block sized units. */ + /** + * Total number of compressed bytes read during cache misses, as block sized + * units. + */ long readBlockBytes; /** Total microseconds spent reading {@link #readBlock} blocks. */ @@ -144,7 +150,8 @@ public class DfsReaderIoStats { } /** - * Get total number of discrete blocks read from pack file(s). + * Get total number of discrete blocks actually read from pack file(s), that + * is, block cache misses. * * @return total number of discrete blocks read from pack file(s). */ @@ -153,7 +160,8 @@ public class DfsReaderIoStats { } /** - * Get total number of compressed bytes read as block sized units. + * Get total number of compressed bytes read during cache misses, as block + * sized units. * * @return total number of compressed bytes read as block sized units. */ @@ -162,7 +170,7 @@ public class DfsReaderIoStats { } /** - * Get total microseconds spent reading blocks. + * Get total microseconds spent reading blocks during cache misses. * * @return total microseconds spent reading blocks. */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java index 9c844ebe06..40cfb71dde 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java @@ -241,7 +241,7 @@ public class DfsReftableDatabase extends DfsRefDatabase { : table.seekRef(prefix)) { while (rc.next()) { Ref ref = table.resolve(rc.getRef()); - if (ref != null) { + if (ref != null && ref.getObjectId() != null) { all.add(ref); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java index 9e2fb55de0..9b82210e6c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java @@ -620,11 +620,8 @@ public class FileRepository extends Repository { static void loadRulesFromFile(AttributesNode r, File attrs) throws FileNotFoundException, IOException { if (attrs.exists()) { - FileInputStream in = new FileInputStream(attrs); - try { + try (FileInputStream in = new FileInputStream(attrs)) { r.parse(in); - } finally { - in.close(); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java index 2895bae205..767fffcf0f 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 @@ -296,7 +296,7 @@ public class GC { packRefs(); // TODO: implement reflog_expire(pm, repo); Collection<PackFile> newPacks = repack(); - prune(Collections.<ObjectId> emptySet()); + prune(Collections.emptySet()); // TODO: implement rerere_gc(pm); return newPacks; } @@ -569,7 +569,6 @@ public class GC { } catch (IllegalArgumentException notAnObject) { // ignoring the file that does not represent loose // object - continue; } } } @@ -736,34 +735,31 @@ public class GC { RevObject ro = w.next(); while (ro != null) { checkCancelled(); - if (id2File.remove(ro.getId()) != null) - if (id2File.isEmpty()) - return; + if (id2File.remove(ro.getId()) != null && id2File.isEmpty()) { + return; + } ro = w.next(); } ro = w.nextObject(); while (ro != null) { checkCancelled(); - if (id2File.remove(ro.getId()) != null) - if (id2File.isEmpty()) - return; + if (id2File.remove(ro.getId()) != null && id2File.isEmpty()) { + return; + } ro = w.nextObject(); } } private static boolean equals(Ref r1, Ref r2) { - if (r1 == null || r2 == null) + if (r1 == null || r2 == null) { return false; + } if (r1.isSymbolic()) { - if (!r2.isSymbolic()) - return false; - return r1.getTarget().getName().equals(r2.getTarget().getName()); - } else { - if (r2.isSymbolic()) { - return false; - } - return Objects.equals(r1.getObjectId(), r2.getObjectId()); + return r2.isSymbolic() && r1.getTarget().getName() + .equals(r2.getTarget().getName()); } + return !r2.isSymbolic() + && Objects.equals(r1.getObjectId(), r2.getObjectId()); } /** @@ -968,11 +964,10 @@ public class GC { List<String> fileNames = null; try (Stream<Path> files = Files.list(packDir)) { fileNames = files.map(path -> path.getFileName().toString()) - .filter(name -> { - return (name.endsWith(PACK_EXT) - || name.endsWith(BITMAP_EXT) - || name.endsWith(INDEX_EXT)); - }).sorted(Collections.reverseOrder()) + .filter(name -> (name.endsWith(PACK_EXT) + || name.endsWith(BITMAP_EXT) + || name.endsWith(INDEX_EXT))) + .sorted(Collections.reverseOrder()) .collect(Collectors.toList()); } catch (IOException e1) { // ignore @@ -988,7 +983,7 @@ public class GC { } else { if (base == null || !n.startsWith(base)) { try { - Files.delete(FileUtils.toPath(new File(packDir.toFile(), n))); + Files.delete(packDir.resolve(n)); } catch (IOException e) { LOG.error(e.getMessage(), e); } @@ -1033,7 +1028,7 @@ public class GC { List<ReflogEntry> rlEntries = reflogReader .getReverseEntries(); if (rlEntries == null || rlEntries.isEmpty()) - return Collections.<ObjectId> emptySet(); + return Collections.emptySet(); Set<ObjectId> ret = new HashSet<>(); for (ReflogEntry e : rlEntries) { if (e.getWho().getWhen().getTime() < minTime) @@ -1137,23 +1132,21 @@ public class GC { throws IOException { checkCancelled(); File tmpPack = null; - Map<PackExt, File> tmpExts = new TreeMap<>( - new Comparator<PackExt>() { - @Override - public int compare(PackExt o1, PackExt o2) { - // INDEX entries must be returned last, so the pack - // scanner does pick up the new pack until all the - // PackExt entries have been written. - if (o1 == o2) - return 0; - if (o1 == PackExt.INDEX) - return 1; - if (o2 == PackExt.INDEX) - return -1; - return Integer.signum(o1.hashCode() - o2.hashCode()); - } - - }); + Map<PackExt, File> tmpExts = new TreeMap<>((o1, o2) -> { + // INDEX entries must be returned last, so the pack + // scanner does pick up the new pack until all the + // PackExt entries have been written. + if (o1 == o2) { + return 0; + } + if (o1 == PackExt.INDEX) { + return 1; + } + if (o2 == PackExt.INDEX) { + return -1; + } + return Integer.signum(o1.hashCode() - o2.hashCode()); + }); try (PackWriter pw = new PackWriter( (pconfig == null) ? new PackConfig(repo) : pconfig, repo.newObjectReader())) { @@ -1185,27 +1178,21 @@ public class GC { JGitText.get().cannotCreateIndexfile, tmpIdx.getPath())); // write the packfile - FileOutputStream fos = new FileOutputStream(tmpPack); - FileChannel channel = fos.getChannel(); - OutputStream channelStream = Channels.newOutputStream(channel); - try { + try (FileOutputStream fos = new FileOutputStream(tmpPack); + FileChannel channel = fos.getChannel(); + OutputStream channelStream = Channels + .newOutputStream(channel)) { pw.writePack(pm, pm, channelStream); - } finally { channel.force(true); - channelStream.close(); - fos.close(); } // write the packindex - fos = new FileOutputStream(tmpIdx); - FileChannel idxChannel = fos.getChannel(); - OutputStream idxStream = Channels.newOutputStream(idxChannel); - try { + try (FileOutputStream fos = new FileOutputStream(tmpIdx); + FileChannel idxChannel = fos.getChannel(); + OutputStream idxStream = Channels + .newOutputStream(idxChannel)) { pw.writeIndex(idxStream); - } finally { idxChannel.force(true); - idxStream.close(); - fos.close(); } if (pw.prepareBitmapIndex(pm)) { @@ -1217,15 +1204,12 @@ public class GC { JGitText.get().cannotCreateIndexfile, tmpBitmapIdx.getPath())); - fos = new FileOutputStream(tmpBitmapIdx); - idxChannel = fos.getChannel(); - idxStream = Channels.newOutputStream(idxChannel); - try { + try (FileOutputStream fos = new FileOutputStream(tmpBitmapIdx); + FileChannel idxChannel = fos.getChannel(); + OutputStream idxStream = Channels + .newOutputStream(idxChannel)) { pw.writeBitmapIndex(idxStream); - } finally { idxChannel.force(true); - idxStream.close(); - fos.close(); } } @@ -1524,8 +1508,8 @@ public class GC { private boolean needGc() { if (tooManyPacks()) { addRepackAllOption(); - } else if (!tooManyLooseObjects()) { - return false; + } else { + return tooManyLooseObjects(); } // TODO run pre-auto-gc hook, if it fails return false return true; @@ -1566,22 +1550,17 @@ public class GC { int n = 0; int threshold = (auto + 255) / 256; Path dir = repo.getObjectsDirectory().toPath().resolve("17"); //$NON-NLS-1$ - if (!Files.exists(dir)) { + if (!dir.toFile().exists()) { return false; } - try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, - new DirectoryStream.Filter<Path>() { - - @Override - public boolean accept(Path file) throws IOException { - Path fileName = file.getFileName(); - return Files.isRegularFile(file) && fileName != null - && PATTERN_LOOSE_OBJECT - .matcher(fileName.toString()).matches(); - } + try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, file -> { + Path fileName = file.getFileName(); + return file.toFile().isFile() && fileName != null + && PATTERN_LOOSE_OBJECT.matcher(fileName.toString()) + .matches(); })) { - for (Iterator<Path> iter = stream.iterator(); iter.hasNext(); - iter.next()) { + for (Iterator<Path> iter = stream.iterator(); iter.hasNext(); iter + .next()) { if (++n > threshold) { return true; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java index 3460913e9c..bc23dcce27 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java @@ -221,8 +221,7 @@ public class LockFile { public void copyCurrentContent() throws IOException { requireLock(); try { - final FileInputStream fis = new FileInputStream(ref); - try { + try (FileInputStream fis = new FileInputStream(ref)) { if (fsync) { FileChannel in = fis.getChannel(); long pos = 0; @@ -238,8 +237,6 @@ public class LockFile { while ((r = fis.read(buf)) >= 0) os.write(buf, 0, r); } - } finally { - fis.close(); } } catch (FileNotFoundException fnfe) { if (ref.exists()) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java index 3e2fd82ce0..92b53ad44a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java @@ -796,8 +796,7 @@ public class ObjectDirectory extends FileObjectDatabase { || shallowFileSnapshot.isModified(shallowFile)) { shallowCommitsIds = new HashSet<>(); - final BufferedReader reader = open(shallowFile); - try { + try (BufferedReader reader = open(shallowFile)) { String line; while ((line = reader.readLine()) != null) { try { @@ -807,8 +806,6 @@ public class ObjectDirectory extends FileObjectDatabase { .format(JGitText.get().badShallowLine, line)); } } - } finally { - reader.close(); } shallowFileSnapshot = FileSnapshot.save(shallowFile); @@ -1027,14 +1024,11 @@ public class ObjectDirectory extends FileObjectDatabase { private AlternateHandle[] loadAlternates() throws IOException { final List<AlternateHandle> l = new ArrayList<>(4); - final BufferedReader br = open(alternatesFile); - try { + try (BufferedReader br = open(alternatesFile)) { String line; while ((line = br.readLine()) != null) { l.add(openAlternate(line)); } - } finally { - br.close(); } return l.toArray(new AlternateHandle[l.size()]); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java index 9d51f4b1de..ad2d89afc5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java @@ -67,6 +67,7 @@ import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.nio.file.DirectoryNotEmptyException; import java.nio.file.Files; +import java.nio.file.Path; import java.security.DigestInputStream; import java.security.MessageDigest; import java.text.MessageFormat; @@ -79,6 +80,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Stream; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.Nullable; @@ -939,12 +941,27 @@ public class RefDirectory extends RefDatabase { int retries = 0; while (true) { final FileSnapshot snapshot = FileSnapshot.save(packedRefsFile); - final BufferedReader br; final MessageDigest digest = Constants.newMessageDigest(); - try { - br = new BufferedReader(new InputStreamReader( - new DigestInputStream(new FileInputStream(packedRefsFile), - digest), CHARSET)); + try (BufferedReader br = new BufferedReader(new InputStreamReader( + new DigestInputStream(new FileInputStream(packedRefsFile), + digest), + CHARSET))) { + try { + return new PackedRefList(parsePackedRefs(br), snapshot, + ObjectId.fromRaw(digest.digest())); + } catch (IOException e) { + if (FileUtils.isStaleFileHandleInCausalChain(e) + && retries < maxStaleRetries) { + if (LOG.isDebugEnabled()) { + LOG.debug(MessageFormat.format( + JGitText.get().packedRefsHandleIsStale, + Integer.valueOf(retries)), e); + } + retries++; + continue; + } + throw e; + } } catch (FileNotFoundException noPackedRefs) { if (packedRefsFile.exists()) { throw noPackedRefs; @@ -952,24 +969,6 @@ public class RefDirectory extends RefDatabase { // Ignore it and leave the new list empty. return NO_PACKED_REFS; } - try { - return new PackedRefList(parsePackedRefs(br), snapshot, - ObjectId.fromRaw(digest.digest())); - } catch (IOException e) { - if (FileUtils.isStaleFileHandleInCausalChain(e) - && retries < maxStaleRetries) { - if (LOG.isDebugEnabled()) { - LOG.debug(MessageFormat.format( - JGitText.get().packedRefsHandleIsStale, - Integer.valueOf(retries)), e); - } - retries++; - continue; - } - throw e; - } finally { - br.close(); - } } } @@ -1221,7 +1220,9 @@ public class RefDirectory extends RefDatabase { } private boolean hasLooseRef() throws IOException { - return Files.walk(refsDir.toPath()).anyMatch(Files::isRegularFile); + try (Stream<Path> stream = Files.walk(refsDir.toPath())) { + return stream.anyMatch(Files::isRegularFile); + } } /** If the parent should fire listeners, fires them. */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java index 213cacab8c..e71284fb62 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java @@ -171,7 +171,10 @@ public class WindowCache { return streamFileThreshold; } - static WindowCache getInstance() { + /** + * @return the cached instance. + */ + public static WindowCache getInstance() { return cache; } @@ -269,11 +272,17 @@ public class WindowCache { throw new IllegalArgumentException(JGitText.get().windowSizeMustBeLesserThanLimit); } - int getOpenFiles() { + /** + * @return the number of open files. + */ + public int getOpenFiles() { return openFiles.get(); } - long getOpenBytes() { + /** + * @return the number of open bytes. + */ + public long getOpenBytes() { return openBytes.get(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java index 71d10ac45d..42df1a6588 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java @@ -81,6 +81,7 @@ import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.LargeObjectException; @@ -355,6 +356,24 @@ public class PackWriter implements AutoCloseable { * reader to read from the repository with. */ public PackWriter(final PackConfig config, final ObjectReader reader) { + this(config, reader, null); + } + + /** + * Create writer with a specified configuration. + * <p> + * Objects for packing are specified in {@link #preparePack(Iterator)} or + * {@link #preparePack(ProgressMonitor, Set, Set)}. + * + * @param config + * configuration for the pack writer. + * @param reader + * reader to read from the repository with. + * @param statsAccumulator + * accumulator for statics + */ + public PackWriter(PackConfig config, final ObjectReader reader, + @Nullable PackStatistics.Accumulator statsAccumulator) { this.config = config; this.reader = reader; if (reader instanceof ObjectReuseAsIs) @@ -365,7 +384,8 @@ public class PackWriter implements AutoCloseable { deltaBaseAsOffset = config.isDeltaBaseAsOffset(); reuseDeltas = config.isReuseDeltas(); reuseValidate = true; // be paranoid by default - stats = new PackStatistics.Accumulator(); + stats = statsAccumulator != null ? statsAccumulator + : new PackStatistics.Accumulator(); state = new MutableState(); selfRef = new WeakReference<>(this); instances.put(selfRef, Boolean.TRUE); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java index baebde2b48..ef686a3008 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java @@ -198,8 +198,8 @@ public class MergedReftable extends Reftable { ref = t.rc.getRef(); updateIndex = t.rc.getUpdateIndex(); boolean include = includeDeletes || !t.rc.wasDeleted(); - skipShadowedRefs(ref.getName()); add(t); + skipShadowedRefs(ref.getName()); if (include) { return true; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchTrackingStatus.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchTrackingStatus.java index ac1529f990..51b5b80fc4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchTrackingStatus.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchTrackingStatus.java @@ -87,22 +87,25 @@ public class BranchTrackingStatus { if (local == null) return null; - RevWalk walk = new RevWalk(repository); + try (RevWalk walk = new RevWalk(repository)) { - RevCommit localCommit = walk.parseCommit(local.getObjectId()); - RevCommit trackingCommit = walk.parseCommit(tracking.getObjectId()); + RevCommit localCommit = walk.parseCommit(local.getObjectId()); + RevCommit trackingCommit = walk.parseCommit(tracking.getObjectId()); - walk.setRevFilter(RevFilter.MERGE_BASE); - walk.markStart(localCommit); - walk.markStart(trackingCommit); - RevCommit mergeBase = walk.next(); + walk.setRevFilter(RevFilter.MERGE_BASE); + walk.markStart(localCommit); + walk.markStart(trackingCommit); + RevCommit mergeBase = walk.next(); - walk.reset(); - walk.setRevFilter(RevFilter.ALL); - int aheadCount = RevWalkUtils.count(walk, localCommit, mergeBase); - int behindCount = RevWalkUtils.count(walk, trackingCommit, mergeBase); + walk.reset(); + walk.setRevFilter(RevFilter.ALL); + int aheadCount = RevWalkUtils.count(walk, localCommit, mergeBase); + int behindCount = RevWalkUtils.count(walk, trackingCommit, + mergeBase); - return new BranchTrackingStatus(trackingBranch, aheadCount, behindCount); + return new BranchTrackingStatus(trackingBranch, aheadCount, + behindCount); + } } private final String remoteTrackingBranch; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java index a6313f0cc5..4d558c9fc2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java @@ -1125,7 +1125,7 @@ public class Config { } else e.value = readValue(in); - if (e.section.equals("include")) { //$NON-NLS-1$ + if (e.section.equalsIgnoreCase("include")) { //$NON-NLS-1$ addIncludedConfig(newEntries, e, depth); } } else @@ -1154,10 +1154,10 @@ public class Config { private void addIncludedConfig(final List<ConfigLine> newEntries, ConfigLine line, int depth) throws ConfigInvalidException { - if (!line.name.equals("path") || //$NON-NLS-1$ + if (!line.name.equalsIgnoreCase("path") || //$NON-NLS-1$ line.value == null || line.value.equals(MAGIC_EMPTY_VALUE)) { - throw new ConfigInvalidException( - JGitText.get().invalidLineInConfigFile); + throw new ConfigInvalidException(MessageFormat.format( + JGitText.get().invalidLineInConfigFileWithParam, line)); } byte[] bytes = readIncludedConfig(line.value); if (bytes == null) { @@ -1171,7 +1171,12 @@ public class Config { } else { decoded = RawParseUtils.decode(bytes); } - newEntries.addAll(fromTextRecurse(decoded, depth + 1)); + try { + newEntries.addAll(fromTextRecurse(decoded, depth + 1)); + } catch (ConfigInvalidException e) { + throw new ConfigInvalidException(MessageFormat + .format(JGitText.get().cannotReadFile, line.value), e); + } } private ConfigSnapshot newState() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java index 08c883a83e..5a790350b1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java @@ -420,4 +420,16 @@ public class ConfigConstants { * @since 4.7 */ public static final String CONFIG_KEY_RECURSE_SUBMODULES = "recurseSubmodules"; + + /** + * The "required" key + * @since 4.11 + */ + public static final String CONFIG_KEY_REQUIRED = "required"; + + /** + * The "lfs" section + * @since 4.11 + */ + public static final String CONFIG_SECTION_LFS = "lfs"; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java index d2160011be..bb85229f82 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -438,6 +438,13 @@ public final class Constants { public static final String ATTR_MERGE = "merge"; //$NON-NLS-1$ /** + * Diff attribute. + * + * @since 4.11 + */ + public static final String ATTR_DIFF = "diff"; //$NON-NLS-1$ + + /** * Binary value for custom merger. * * @since 4.9 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSerializer.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSerializer.java new file mode 100644 index 0000000000..4f8bd326b7 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSerializer.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * Copyright (C) 2009, Shawn O. Pearce <spearce@spearce.org> + * Copyright (C) 2018, David Pursehouse <david.pursehouse@gmail.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.lib; + +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.util.IO; + +/** + * Helper to serialize {@link ObjectId} instances. {@link ObjectId} is already + * serializable, but this class provides methods to handle null and non-null + * instances. + * + * @since 4.11 + */ +public class ObjectIdSerializer { + /* + * Marker to indicate a null ObjectId instance. + */ + private static final byte NULL_MARKER = 0; + + /* + * Marker to indicate a non-null ObjectId instance. + */ + private static final byte NON_NULL_MARKER = 1; + + /** + * Write a possibly null {@link ObjectId} to the stream, using markers to + * differentiate null and non-null instances. + * + * <p> + * If the id is non-null, writes a {@link #NON_NULL_MARKER} followed by the + * id's words. If it is null, writes a {@link #NULL_MARKER} and nothing + * else. + * + * @param out + * the output stream + * @param id + * the object id to serialize; may be null + * @throws IOException + * the stream writing failed + */ + public static void write(OutputStream out, @Nullable AnyObjectId id) + throws IOException { + if (id != null) { + out.write(NON_NULL_MARKER); + writeWithoutMarker(out, id); + } else { + out.write(NULL_MARKER); + } + } + + /** + * Write a non-null {@link ObjectId} to the stream. + * + * @param out + * the output stream + * @param id + * the object id to serialize; never null + * @throws IOException + * the stream writing failed + * @since 4.11 + */ + public static void writeWithoutMarker(OutputStream out, @NonNull AnyObjectId id) + throws IOException { + id.copyRawTo(out); + } + + /** + * Read a possibly null {@link ObjectId} from the stream. + * + * Reads the first byte of the stream, which is expected to be either + * {@link #NON_NULL_MARKER} or {@link #NULL_MARKER}. + * + * @param in + * the input stream + * @return the object id, or null + * @throws IOException + * there was an error reading the stream + */ + @Nullable + public static ObjectId read(InputStream in) throws IOException { + byte marker = (byte) in.read(); + switch (marker) { + case NULL_MARKER: + return null; + case NON_NULL_MARKER: + return readWithoutMarker(in); + default: + throw new IOException("Invalid flag before ObjectId: " + marker); //$NON-NLS-1$ + } + } + + /** + * Read a non-null {@link ObjectId} from the stream. + * + * @param in + * the input stream + * @return the object id; never null + * @throws IOException + * there was an error reading the stream + * @since 4.11 + */ + @NonNull + public static ObjectId readWithoutMarker(InputStream in) throws IOException { + final byte[] b = new byte[OBJECT_ID_LENGTH]; + IO.readFully(in, b, 0, OBJECT_ID_LENGTH); + return ObjectId.fromRaw(b); + } + + private ObjectIdSerializer() { + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index 8c448af904..4c0bde1376 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -1104,7 +1104,8 @@ public abstract class Repository implements AutoCloseable { } /** - * Get mutable map of all known refs + * Get mutable map of all known refs, including symrefs like HEAD that may + * not point to any object yet. * * @return mutable map of all known refs (heads, tags, remotes). */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java index 90ef5c779f..32ef504802 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java @@ -180,8 +180,8 @@ public class TagBuilder { */ public byte[] build() { ByteArrayOutputStream os = new ByteArrayOutputStream(); - OutputStreamWriter w = new OutputStreamWriter(os, Constants.CHARSET); - try { + try (OutputStreamWriter w = new OutputStreamWriter(os, + Constants.CHARSET)) { w.write("object "); //$NON-NLS-1$ getObjectId().copyTo(w); w.write('\n'); @@ -203,7 +203,6 @@ public class TagBuilder { w.write('\n'); if (getMessage() != null) w.write(getMessage()); - w.close(); } catch (IOException err) { // This should never occur, the only way to get it above is // for the ByteArrayOutputStream to throw, but it doesn't. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java index 6d43f09c98..6f7a702470 100644..100755 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -3,6 +3,7 @@ * Copyright (C) 2010-2012, Matthias Sohn <matthias.sohn@sap.com> * Copyright (C) 2012, Research In Motion Limited * Copyright (C) 2017, Obeo (mathieu.cartaud@obeo.fr) + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -53,7 +54,6 @@ import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -87,22 +87,29 @@ import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.NoWorkTreeException; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectLoader; -import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.storage.pack.PackConfig; +import org.eclipse.jgit.submodule.SubmoduleConflict; import org.eclipse.jgit.treewalk.AbstractTreeIterator; import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.eclipse.jgit.treewalk.NameConflictTreeWalk; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.TreeWalk.OperationType; import org.eclipse.jgit.treewalk.WorkingTreeIterator; +import org.eclipse.jgit.treewalk.WorkingTreeOptions; import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.LfsFactory; +import org.eclipse.jgit.util.LfsFactory.LfsInputStream; import org.eclipse.jgit.util.TemporaryBuffer; +import org.eclipse.jgit.util.io.EolStreamTypeUtil; /** * A three-way merger performing a content-merge if necessary @@ -277,8 +284,16 @@ public class ResolveMerger extends ThreeWayMerger { protected MergeAlgorithm mergeAlgorithm; /** - * The size limit (bytes) which controls a file to be stored in {@code Heap} or - * {@code LocalFile} during the merge. + * The {@link WorkingTreeOptions} are needed to determine line endings for + * merged files. + * + * @since 4.11 + */ + protected WorkingTreeOptions workingTreeOptions; + + /** + * The size limit (bytes) which controls a file to be stored in {@code Heap} + * or {@code LocalFile} during the merge. */ private int inCoreLimit; @@ -319,6 +334,7 @@ public class ResolveMerger extends ThreeWayMerger { dircache = DirCache.newInCore(); } else { implicitDirCache = true; + workingTreeOptions = local.getConfig().get(WorkingTreeOptions.KEY); } } @@ -380,8 +396,13 @@ public class ResolveMerger extends ThreeWayMerger { } for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut .entrySet()) { - DirCacheCheckout.checkoutEntry(db, entry.getValue(), reader); - modifiedFiles.add(entry.getKey()); + DirCacheEntry cacheEntry = entry.getValue(); + if (cacheEntry.getFileMode() == FileMode.GITLINK) { + new File(nonNullRepo().getWorkTree(), entry.getKey()).mkdirs(); + } else { + DirCacheCheckout.checkoutEntry(db, cacheEntry, reader); + modifiedFiles.add(entry.getKey()); + } } } @@ -712,24 +733,50 @@ public class ResolveMerger extends ThreeWayMerger { if (nonTree(modeO) && nonTree(modeT)) { // Check worktree before modifying files - if (isWorktreeDirty(work, ourDce)) + boolean worktreeDirty = isWorktreeDirty(work, ourDce); + if (!attributes.canBeContentMerged() && worktreeDirty) { return false; + } + boolean gitlinkConflict = isGitLink(modeO) || isGitLink(modeT); // Don't attempt to resolve submodule link conflicts - if (isGitLink(modeO) || isGitLink(modeT) - || !attributes.canBeContentMerged()) { + if (gitlinkConflict || !attributes.canBeContentMerged()) { add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0); add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0); add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0); - unmergedPaths.add(tw.getPathString()); + + if (gitlinkConflict) { + MergeResult<SubmoduleConflict> result = new MergeResult<>( + Arrays.asList( + new SubmoduleConflict(base == null ? null + : base.getEntryObjectId()), + new SubmoduleConflict(ours == null ? null + : ours.getEntryObjectId()), + new SubmoduleConflict(theirs == null ? null + : theirs.getEntryObjectId()))); + result.setContainsConflicts(true); + mergeResults.put(tw.getPathString(), result); + if (!ignoreConflicts) { + unmergedPaths.add(tw.getPathString()); + } + } else { + // attribute merge issues are conflicts but not failures + unmergedPaths.add(tw.getPathString()); + } return true; } - MergeResult<RawText> result = contentMerge(base, ours, theirs); + // Check worktree before modifying files + if (worktreeDirty) { + return false; + } + + MergeResult<RawText> result = contentMerge(base, ours, theirs, + attributes); if (ignoreConflicts) { result.setContainsConflicts(false); } - updateIndex(base, ours, theirs, result); + updateIndex(base, ours, theirs, result, attributes); if (result.containsConflicts() && !ignoreConflicts) unmergedPaths.add(tw.getPathString()); modifiedFiles.add(tw.getPathString()); @@ -737,7 +784,8 @@ public class ResolveMerger extends ThreeWayMerger { // OURS or THEIRS has been deleted if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw .idEqual(T_BASE, T_THEIRS)))) { - MergeResult<RawText> result = contentMerge(base, ours, theirs); + MergeResult<RawText> result = contentMerge(base, ours, theirs, + attributes); add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0); add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0); @@ -772,12 +820,14 @@ public class ResolveMerger extends ThreeWayMerger { * @param base * @param ours * @param theirs + * @param attributes * * @return the result of the content merge * @throws IOException */ private MergeResult<RawText> contentMerge(CanonicalTreeParser base, - CanonicalTreeParser ours, CanonicalTreeParser theirs) + CanonicalTreeParser ours, CanonicalTreeParser theirs, + Attributes attributes) throws IOException { RawText baseText; RawText ourText; @@ -785,11 +835,11 @@ public class ResolveMerger extends ThreeWayMerger { try { baseText = base == null ? RawText.EMPTY_TEXT : getRawText( - base.getEntryObjectId(), reader); + base.getEntryObjectId(), attributes); ourText = ours == null ? RawText.EMPTY_TEXT : getRawText( - ours.getEntryObjectId(), reader); + ours.getEntryObjectId(), attributes); theirsText = theirs == null ? RawText.EMPTY_TEXT : getRawText( - theirs.getEntryObjectId(), reader); + theirs.getEntryObjectId(), attributes); } catch (BinaryBlobException e) { MergeResult<RawText> r = new MergeResult<>(Collections.<RawText>emptyList()); r.setContainsConflicts(true); @@ -853,80 +903,88 @@ public class ResolveMerger extends ThreeWayMerger { * @param ours * @param theirs * @param result + * @param attributes * @throws FileNotFoundException * @throws IOException */ private void updateIndex(CanonicalTreeParser base, CanonicalTreeParser ours, CanonicalTreeParser theirs, - MergeResult<RawText> result) throws FileNotFoundException, + MergeResult<RawText> result, Attributes attributes) + throws FileNotFoundException, IOException { - File mergedFile = !inCore ? writeMergedFile(result) : null; - - if (result.containsConflicts()) { - // A conflict occurred, the file will contain conflict markers - // the index will be populated with the three stages and the - // workdir (if used) contains the halfway merged content. - add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0); - add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0); - add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0); - mergeResults.put(tw.getPathString(), result); - return; - } + TemporaryBuffer rawMerged = null; + try { + rawMerged = doMerge(result); + File mergedFile = inCore ? null + : writeMergedFile(rawMerged, attributes); + if (result.containsConflicts()) { + // A conflict occurred, the file will contain conflict markers + // the index will be populated with the three stages and the + // workdir (if used) contains the halfway merged content. + add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0); + add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0); + add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0); + mergeResults.put(tw.getPathString(), result); + return; + } - // No conflict occurred, the file will contain fully merged content. - // The index will be populated with the new merged version. - DirCacheEntry dce = new DirCacheEntry(tw.getPathString()); - - // Set the mode for the new content. Fall back to REGULAR_FILE if - // we can't merge modes of OURS and THEIRS. - int newMode = mergeFileModes( - tw.getRawMode(0), - tw.getRawMode(1), - tw.getRawMode(2)); - dce.setFileMode(newMode == FileMode.MISSING.getBits() - ? FileMode.REGULAR_FILE - : FileMode.fromBits(newMode)); - if (mergedFile != null) { - long len = mergedFile.length(); - dce.setLastModified(FS.DETECTED.lastModified(mergedFile)); - dce.setLength((int) len); - InputStream is = new FileInputStream(mergedFile); - try { - dce.setObjectId(getObjectInserter().insert(OBJ_BLOB, len, is)); - } finally { - is.close(); + // No conflict occurred, the file will contain fully merged content. + // The index will be populated with the new merged version. + DirCacheEntry dce = new DirCacheEntry(tw.getPathString()); + + // Set the mode for the new content. Fall back to REGULAR_FILE if + // we can't merge modes of OURS and THEIRS. + int newMode = mergeFileModes(tw.getRawMode(0), tw.getRawMode(1), + tw.getRawMode(2)); + dce.setFileMode(newMode == FileMode.MISSING.getBits() + ? FileMode.REGULAR_FILE : FileMode.fromBits(newMode)); + if (mergedFile != null) { + dce.setLastModified( + nonNullRepo().getFS().lastModified(mergedFile)); + dce.setLength((int) mergedFile.length()); + } + dce.setObjectId(insertMergeResult(rawMerged, attributes)); + builder.add(dce); + } finally { + if (rawMerged != null) { + rawMerged.destroy(); } - } else - dce.setObjectId(insertMergeResult(result)); - builder.add(dce); + } } /** * Writes merged file content to the working tree. * - * @param result - * the result of the content merge + * @param rawMerged + * the raw merged content + * @param attributes + * the files .gitattributes entries * @return the working tree file to which the merged content was written. * @throws FileNotFoundException * @throws IOException */ - private File writeMergedFile(MergeResult<RawText> result) + private File writeMergedFile(TemporaryBuffer rawMerged, + Attributes attributes) throws FileNotFoundException, IOException { File workTree = nonNullRepo().getWorkTree(); FS fs = nonNullRepo().getFS(); File of = new File(workTree, tw.getPathString()); File parentFolder = of.getParentFile(); - if (!fs.exists(parentFolder)) + if (!fs.exists(parentFolder)) { parentFolder.mkdirs(); - try (OutputStream os = new BufferedOutputStream( - new FileOutputStream(of))) { - new MergeFormatter().formatMerge(os, result, - Arrays.asList(commitNames), CHARACTER_ENCODING); + } + EolStreamType streamType = EolStreamTypeUtil.detectStreamType( + OperationType.CHECKOUT_OP, workingTreeOptions, + attributes); + try (OutputStream os = EolStreamTypeUtil.wrapOutputStream( + new BufferedOutputStream(new FileOutputStream(of)), + streamType)) { + rawMerged.writeTo(os, null); } return of; } - private ObjectId insertMergeResult(MergeResult<RawText> result) + private TemporaryBuffer doMerge(MergeResult<RawText> result) throws IOException { TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile( db != null ? nonNullRepo().getDirectory() : null, inCoreLimit); @@ -934,11 +992,20 @@ public class ResolveMerger extends ThreeWayMerger { new MergeFormatter().formatMerge(buf, result, Arrays.asList(commitNames), CHARACTER_ENCODING); buf.close(); - try (InputStream in = buf.openInputStream()) { - return getObjectInserter().insert(OBJ_BLOB, buf.length(), in); - } - } finally { + } catch (IOException e) { buf.destroy(); + throw e; + } + return buf; + } + + private ObjectId insertMergeResult(TemporaryBuffer buf, + Attributes attributes) throws IOException { + InputStream in = buf.openInputStream(); + try (LfsInputStream is = LfsFactory.getInstance().applyCleanFilter( + getRepository(), in, + buf.length(), attributes.get(Constants.ATTR_MERGE))) { + return getObjectInserter().insert(OBJ_BLOB, is.getLength(), is); } } @@ -970,12 +1037,15 @@ public class ResolveMerger extends ThreeWayMerger { return FileMode.MISSING.getBits(); } - private static RawText getRawText(ObjectId id, ObjectReader reader) + private RawText getRawText(ObjectId id, + Attributes attributes) throws IOException, BinaryBlobException { if (id.equals(ObjectId.zeroId())) return new RawText(new byte[] {}); - ObjectLoader loader = reader.open(id, OBJ_BLOB); + ObjectLoader loader = LfsFactory.getInstance().applySmudgeFilter( + getRepository(), reader.open(id, OBJ_BLOB), + attributes.get(Constants.ATTR_MERGE)); int threshold = PackConfig.DEFAULT_BIG_FILE_THRESHOLD; return RawText.load(loader, threshold); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/DefaultNoteMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/DefaultNoteMerger.java index 87bd4b55b1..54a2d8996a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/DefaultNoteMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/DefaultNoteMerger.java @@ -82,14 +82,11 @@ public class DefaultNoteMerger implements NoteMerger { ObjectLoader lo = reader.open(ours.getData()); ObjectLoader lt = reader.open(theirs.getData()); - UnionInputStream union = new UnionInputStream(lo.openStream(), - lt.openStream()); - try { + try (UnionInputStream union = new UnionInputStream(lo.openStream(), + lt.openStream())) { ObjectId noteData = inserter.insert(Constants.OBJ_BLOB, lo.getSize() + lt.getSize(), union); return new Note(ours, noteData); - } finally { - union.close(); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java index 05fab92e8b..da123a7c2d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java @@ -152,10 +152,10 @@ public class Patch { } private static byte[] readFully(final InputStream is) throws IOException { - TemporaryBuffer b = new TemporaryBuffer.Heap(Integer.MAX_VALUE); - b.copy(is); - b.close(); - return b.toByteArray(); + try (TemporaryBuffer b = new TemporaryBuffer.Heap(Integer.MAX_VALUE)) { + b.copy(is); + return b.toByteArray(); + } } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java index 5568241b67..91c15a1e70 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java @@ -53,6 +53,7 @@ import java.util.EnumSet; import java.util.Iterator; import java.util.List; +import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.LargeObjectException; @@ -513,6 +514,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { * * @return the current filter. Never null as a filter is always needed. */ + @NonNull public RevFilter getRevFilter() { return filter; } @@ -552,6 +554,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { * {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} is * returned. */ + @NonNull public TreeFilter getTreeFilter() { return treeFilter; } @@ -649,6 +652,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { * name of the blob object. * @return reference to the blob object. Never null. */ + @NonNull public RevBlob lookupBlob(final AnyObjectId id) { RevBlob c = (RevBlob) objects.get(id); if (c == null) { @@ -668,6 +672,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { * name of the tree object. * @return reference to the tree object. Never null. */ + @NonNull public RevTree lookupTree(final AnyObjectId id) { RevTree c = (RevTree) objects.get(id); if (c == null) { @@ -690,6 +695,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { * name of the commit object. * @return reference to the commit object. Never null. */ + @NonNull public RevCommit lookupCommit(final AnyObjectId id) { RevCommit c = (RevCommit) objects.get(id); if (c == null) { @@ -709,6 +715,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { * name of the tag object. * @return reference to the tag object. Never null. */ + @NonNull public RevTag lookupTag(final AnyObjectId id) { RevTag c = (RevTag) objects.get(id); if (c == null) { @@ -730,6 +737,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { * type of the object. Must be a valid Git object type. * @return reference to the object. Never null. */ + @NonNull public RevObject lookupAny(final AnyObjectId id, final int type) { RevObject r = objects.get(id); if (r == null) { @@ -784,6 +792,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { * @throws java.io.IOException * a pack file or loose object could not be read. */ + @NonNull public RevCommit parseCommit(final AnyObjectId id) throws MissingObjectException, IncorrectObjectTypeException, IOException { @@ -811,6 +820,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { * @throws java.io.IOException * a pack file or loose object could not be read. */ + @NonNull public RevTree parseTree(final AnyObjectId id) throws MissingObjectException, IncorrectObjectTypeException, IOException { @@ -845,6 +855,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { * @throws java.io.IOException * a pack file or loose object could not be read. */ + @NonNull public RevTag parseTag(final AnyObjectId id) throws MissingObjectException, IncorrectObjectTypeException, IOException { RevObject c = parseAny(id); @@ -870,6 +881,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { * @throws java.io.IOException * a pack file or loose object could not be read. */ + @NonNull public RevObject parseAny(final AnyObjectId id) throws MissingObjectException, IOException { RevObject r = objects.get(id); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java index 3ab96ca228..f82301a82d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java @@ -93,27 +93,25 @@ class RewriteGenerator extends Generator { @Override RevCommit next() throws MissingObjectException, IncorrectObjectTypeException, IOException { - for (;;) { - final RevCommit c = source.next(); - if (c == null) - return null; - - boolean rewrote = false; - final RevCommit[] pList = c.parents; - final int nParents = pList.length; - for (int i = 0; i < nParents; i++) { - final RevCommit oldp = pList[i]; - final RevCommit newp = rewrite(oldp); - if (oldp != newp) { - pList[i] = newp; - rewrote = true; - } + final RevCommit c = source.next(); + if (c == null) { + return null; + } + boolean rewrote = false; + final RevCommit[] pList = c.parents; + final int nParents = pList.length; + for (int i = 0; i < nParents; i++) { + final RevCommit oldp = pList[i]; + final RevCommit newp = rewrite(oldp); + if (oldp != newp) { + pList[i] = newp; + rewrote = true; } - if (rewrote) - c.parents = cleanup(pList); - - return c; } + if (rewrote) { + c.parents = cleanup(pList); + } + return c; } private RevCommit rewrite(RevCommit p) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java new file mode 100644 index 0000000000..3570733e41 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2018, David Pursehouse <david.pursehouse@gmail.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.storage.file; + +import org.eclipse.jgit.internal.storage.file.WindowCache; + +/** + * Accessor for stats about {@link WindowCache}. + * + * @since 4.11 + * + */ +public class WindowCacheStats { + /** + * @return the number of open files. + */ + public static int getOpenFiles() { + return WindowCache.getInstance().getOpenFiles(); + } + + /** + * @return the number of open bytes. + */ + public static long getOpenBytes() { + return WindowCache.getInstance().getOpenBytes(); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java index 68cffd5b38..68878e5a61 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java @@ -166,6 +166,36 @@ public class PackStatistics { * POJO for accumulating the statistics. */ public static class Accumulator { + /** + * The count of references in the ref advertisement. + * + * @since 4.11 + */ + public long advertised; + + /** + * The count of client wants. + * + * @since 4.11 + */ + public long wants; + + /** + * The count of client haves. + * + * @since 4.11 + */ + public long haves; + + /** + * Time in ms spent in the negotiation phase. For non-bidirectional + * transports (e.g., HTTP), this is only for the final request that + * sends back the pack file. + * + * @since 4.11 + */ + public long timeNegotiating; + /** The set of objects to be included in the pack. */ public Set<ObjectId> interestingObjects; @@ -271,6 +301,48 @@ public class PackStatistics { } /** + * Get the count of references in the ref advertisement. + * + * @return count of refs in the ref advertisement. + * @since 4.11 + */ + public long getAdvertised() { + return statistics.advertised; + } + + /** + * Get the count of client wants. + * + * @return count of client wants. + * @since 4.11 + */ + public long getWants() { + return statistics.wants; + } + + /** + * Get the count of client haves. + * + * @return count of client haves. + * @since 4.11 + */ + public long getHaves() { + return statistics.haves; + } + + /** + * Time in ms spent in the negotiation phase. For non-bidirectional + * transports (e.g., HTTP), this is only for the final request that sends + * back the pack file. + * + * @return time for ref advertisement in ms. + * @since 4.11 + */ + public long getTimeNegotiating() { + return statistics.timeNegotiating; + } + + /** * Get unmodifiable collection of objects to be included in the pack. * * @return unmodifiable collection of objects to be included in the pack. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleConflict.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleConflict.java new file mode 100644 index 0000000000..856eb725dd --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleConflict.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017, Two Sigma Open Source + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. +* + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.submodule; + +import org.eclipse.jgit.diff.Sequence; +import org.eclipse.jgit.lib.ObjectId; + +/** + * Merges expect that conflicts will consist of Sequences, but that doesn't + * really make sense for submodules. So this represents a submodule conflict. + * + * @since 4.11 + */ +public class SubmoduleConflict extends Sequence { + private final ObjectId objectId; + + /** + * Create a SubmoduleConflict for the given submodule object id + * @param objectId + */ + public SubmoduleConflict(ObjectId objectId) { + super(); + this.objectId = objectId; + } + + @Override + public int size() { + return 1; + } + + /** + * @return the object id for the conflicting submodule + */ + public ObjectId getObjectId() { + return objectId; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java index fee71eb362..d7c5b9d7f9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java @@ -402,9 +402,9 @@ public class AmazonS3 { // We have to copy to produce the cipher text anyway so use // the large object code path as it supports that behavior. // - final OutputStream os = beginPut(bucket, key, null, null); - os.write(data); - os.close(); + try (OutputStream os = beginPut(bucket, key, null, null)) { + os.write(data); + } return; } @@ -418,11 +418,8 @@ public class AmazonS3 { authorize(c); c.setDoOutput(true); c.setFixedLengthStreamingMode(data.length); - final OutputStream os = c.getOutputStream(); - try { + try (OutputStream os = c.getOutputStream()) { os.write(data); - } finally { - os.close(); } switch (HttpSupport.response(c)) { @@ -503,12 +500,10 @@ public class AmazonS3 { authorize(c); c.setDoOutput(true); monitor.beginTask(monitorTask, (int) (len / 1024)); - final OutputStream os = c.getOutputStream(); - try { + try (OutputStream os = c.getOutputStream()) { buf.writeTo(os, monitor); } finally { monitor.endTask(); - os.close(); } switch (HttpSupport.response(c)) { @@ -655,11 +650,8 @@ public class AmazonS3 { static Properties properties(final File authFile) throws FileNotFoundException, IOException { final Properties p = new Properties(); - final FileInputStream in = new FileInputStream(authFile); - try { + try (FileInputStream in = new FileInputStream(authFile)) { p.load(in); - } finally { - in.close(); } return p; } @@ -702,16 +694,13 @@ public class AmazonS3 { throw new IOException(JGitText.get().noXMLParserAvailable); } xr.setContentHandler(this); - final InputStream in = c.getInputStream(); - try { + try (InputStream in = c.getInputStream()) { xr.parse(new InputSource(in)); } catch (SAXException parsingError) { throw new IOException( MessageFormat.format( JGitText.get().errorListing, prefix), parsingError); - } finally { - in.close(); } return; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java index 19d8abe9e0..1383045031 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java @@ -54,6 +54,7 @@ import java.text.MessageFormat; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -230,6 +231,8 @@ public abstract class BasePackFetchConnection extends BasePackConnection private boolean noProgress; + private Set<AnyObjectId> minimalNegotiationSet; + private String lockMessage; private PackLock packLock; @@ -249,8 +252,11 @@ public abstract class BasePackFetchConnection extends BasePackConnection super(packTransport); if (local != null) { - final FetchConfig cfg = local.getConfig().get(FetchConfig::new); + final FetchConfig cfg = getFetchConfig(); allowOfsDelta = cfg.allowOfsDelta; + if (cfg.minimalNegotiation) { + minimalNegotiationSet = new HashSet<>(); + } } else { allowOfsDelta = true; } @@ -277,11 +283,20 @@ public abstract class BasePackFetchConnection extends BasePackConnection } } - private static class FetchConfig { + static class FetchConfig { final boolean allowOfsDelta; + final boolean minimalNegotiation; + FetchConfig(final Config c) { allowOfsDelta = c.getBoolean("repack", "usedeltabaseoffset", true); //$NON-NLS-1$ //$NON-NLS-2$ + minimalNegotiation = c.getBoolean("fetch", "useminimalnegotiation", //$NON-NLS-1$ //$NON-NLS-2$ + false); + } + + FetchConfig(boolean allowOfsDelta, boolean minimalNegotiation) { + this.allowOfsDelta = allowOfsDelta; + this.minimalNegotiation = minimalNegotiation; } } @@ -391,6 +406,10 @@ public abstract class BasePackFetchConnection extends BasePackConnection super.close(); } + FetchConfig getFetchConfig() { + return local.getConfig().get(FetchConfig::new); + } + private int maxTimeWanted(final Collection<Ref> wants) { int maxTime = 0; for (final Ref r : wants) { @@ -492,9 +511,19 @@ public abstract class BasePackFetchConnection extends BasePackConnection } line.append('\n'); p.writeString(line.toString()); + if (minimalNegotiationSet != null) { + Ref current = local.exactRef(r.getName()); + if (current != null) { + ObjectId o = current.getObjectId(); + if (o != null && !o.equals(ObjectId.zeroId())) { + minimalNegotiationSet.add(o); + } + } + } } - if (first) + if (first) { return false; + } p.end(); outNeedsEnd = false; return true; @@ -549,18 +578,24 @@ public abstract class BasePackFetchConnection extends BasePackConnection boolean receivedAck = false; boolean receivedReady = false; - if (statelessRPC) + if (statelessRPC) { state.writeTo(out, null); + } negotiateBegin(); SEND_HAVES: for (;;) { final RevCommit c = walk.next(); - if (c == null) + if (c == null) { break SEND_HAVES; + } - pckOut.writeString("have " + c.getId().name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ + ObjectId o = c.getId(); + pckOut.writeString("have " + o.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ havesSent++; havesSinceLastContinue++; + if (minimalNegotiationSet != null) { + minimalNegotiationSet.remove(o); + } if ((31 & havesSent) != 0) { // We group the have lines into blocks of 32, each marked @@ -570,8 +605,9 @@ public abstract class BasePackFetchConnection extends BasePackConnection continue; } - if (monitor.isCancelled()) + if (monitor.isCancelled()) { throw new CancelledException(); + } pckOut.end(); resultsPending++; // Each end will cause a result to come back. @@ -593,6 +629,16 @@ public abstract class BasePackFetchConnection extends BasePackConnection // pack on the remote side. Keep doing that. // resultsPending--; + if (minimalNegotiationSet != null + && minimalNegotiationSet.isEmpty()) { + // Minimal negotiation was requested and we sent out our + // current reference values for our wants, so terminate + // negotiation early. + if (statelessRPC) { + state.writeTo(out, null); + } + break SEND_HAVES; + } break READ_RESULT; case ACK: @@ -603,8 +649,9 @@ public abstract class BasePackFetchConnection extends BasePackConnection multiAck = MultiAck.OFF; resultsPending = 0; receivedAck = true; - if (statelessRPC) + if (statelessRPC) { state.writeTo(out, null); + } break SEND_HAVES; case ACK_CONTINUE: @@ -619,19 +666,31 @@ public abstract class BasePackFetchConnection extends BasePackConnection receivedAck = true; receivedContinue = true; havesSinceLastContinue = 0; - if (anr == AckNackResult.ACK_READY) + if (anr == AckNackResult.ACK_READY) { receivedReady = true; + } + if (minimalNegotiationSet != null && minimalNegotiationSet.isEmpty()) { + // Minimal negotiation was requested and we sent out our current reference + // values for our wants, so terminate negotiation early. + if (statelessRPC) { + state.writeTo(out, null); + } + break SEND_HAVES; + } break; } - if (monitor.isCancelled()) + if (monitor.isCancelled()) { throw new CancelledException(); + } } - if (noDone & receivedReady) + if (noDone & receivedReady) { break SEND_HAVES; - if (statelessRPC) + } + if (statelessRPC) { state.writeTo(out, null); + } if (receivedContinue && havesSinceLastContinue > MAX_HAVES) { // Our history must be really different from the remote's. @@ -645,8 +704,9 @@ public abstract class BasePackFetchConnection extends BasePackConnection // Tell the remote side we have run out of things to talk about. // - if (monitor.isCancelled()) + if (monitor.isCancelled()) { throw new CancelledException(); + } if (!receivedReady || !noDone) { // When statelessRPC is true we should always leave SEND_HAVES @@ -691,8 +751,9 @@ public abstract class BasePackFetchConnection extends BasePackConnection break; } - if (monitor.isCancelled()) + if (monitor.isCancelled()) { throw new CancelledException(); + } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java index e0ec7751b9..712eb22152 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java @@ -150,23 +150,15 @@ public abstract class DaemonService { throws IOException, ServiceNotEnabledException, ServiceNotAuthorizedException { final String name = commandLine.substring(command.length() + 1); - Repository db; - try { - db = client.getDaemon().openRepository(client, name); + try (Repository db = client.getDaemon().openRepository(client, name)) { + if (isEnabledFor(db)) { + execute(client, db); + } } catch (ServiceMayNotContinueException e) { // An error when opening the repo means the client is expecting a ref // advertisement, so use that style of error. PacketLineOut pktOut = new PacketLineOut(client.getOutputStream()); pktOut.writeString("ERR " + e.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ - db = null; - } - if (db == null) - return; - try { - if (isEnabledFor(db)) - execute(client, db); - } finally { - db.close(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java index ed10f449df..117e8447f2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java @@ -336,14 +336,12 @@ class FetchProcess { final LockFile lock = new LockFile(new File(meta, "FETCH_HEAD")); //$NON-NLS-1$ try { if (lock.lock()) { - final Writer w = new OutputStreamWriter(lock.getOutputStream()); - try { + try (Writer w = new OutputStreamWriter( + lock.getOutputStream())) { for (final FetchHeadRecord h : fetchHeadUpdates) { h.write(w); result.add(h); } - } finally { - w.close(); } lock.commit(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java index c38182741d..2a222fbc7b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java @@ -362,13 +362,8 @@ public abstract class JschConfigSessionFactory extends SshSessionFactory { if (home == null) return; final File known_hosts = new File(new File(home, ".ssh"), "known_hosts"); //$NON-NLS-1$ //$NON-NLS-2$ - try { - final FileInputStream in = new FileInputStream(known_hosts); - try { - sch.setKnownHosts(in); - } finally { - in.close(); - } + try (FileInputStream in = new FileInputStream(known_hosts)) { + sch.setKnownHosts(in); } catch (FileNotFoundException none) { // Oh well. They don't have a known hosts in home. } catch (IOException err) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java index 25486cb64f..15338a3a3a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java @@ -76,8 +76,9 @@ import org.eclipse.jgit.util.RawParseUtils; * an unrecoverable error. * * @see SideBandOutputStream + * @since 4.11 */ -class SideBandInputStream extends InputStream { +public class SideBandInputStream extends InputStream { static final int CH_DATA = 1; static final int CH_PROGRESS = 2; static final int CH_ERROR = 3; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java index b186f9ecd3..cdcd2a3fbe 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java @@ -54,6 +54,7 @@ import org.eclipse.jgit.errors.NotSupportedException; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.BasePackFetchConnection.FetchConfig; import org.eclipse.jgit.transport.resolver.ReceivePackFactory; import org.eclipse.jgit.transport.resolver.UploadPackFactory; @@ -78,6 +79,8 @@ import org.eclipse.jgit.transport.resolver.UploadPackFactory; public class TestProtocol<C> extends TransportProtocol { private static final String SCHEME = "test"; //$NON-NLS-1$ + private static FetchConfig fetchConfig; + private class Handle { final C req; final Repository remote; @@ -147,6 +150,10 @@ public class TestProtocol<C> extends TransportProtocol { return Collections.emptySet(); } + static void setFetchConfig(FetchConfig c) { + fetchConfig = c; + } + /** * Register a repository connection over the internal test protocol. * @@ -184,8 +191,14 @@ public class TestProtocol<C> extends TransportProtocol { public FetchConnection openFetch() throws NotSupportedException, TransportException { handle.remote.incrementOpen(); - return new InternalFetchConnection<>( - this, uploadPackFactory, handle.req, handle.remote); + return new InternalFetchConnection<C>(this, uploadPackFactory, + handle.req, handle.remote) { + @Override + FetchConfig getFetchConfig() { + return fetchConfig != null ? fetchConfig + : super.getFetchConfig(); + } + }; } @Override diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java index db43edc4ca..a1826231ab 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java @@ -371,12 +371,9 @@ public class TransportHttp extends HttpTransport implements WalkTransport, private WalkFetchConnection newDumbConnection(InputStream in) throws IOException, PackProtocolException { HttpObjectDB d = new HttpObjectDB(objectsUrl); - BufferedReader br = toBufferedReader(in); Map<String, Ref> refs; - try { + try (BufferedReader br = toBufferedReader(in)) { refs = d.readAdvertisedImpl(br); - } finally { - br.close(); } if (!refs.containsKey(HEAD)) { @@ -391,8 +388,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport, int status = HttpSupport.response(conn); switch (status) { case HttpConnection.HTTP_OK: { - br = toBufferedReader(openInputStream(conn)); - try { + try (BufferedReader br = toBufferedReader( + openInputStream(conn))) { String line = br.readLine(); if (line != null && line.startsWith(RefDirectory.SYMREF)) { String target = line.substring(RefDirectory.SYMREF.length()); @@ -406,8 +403,6 @@ public class TransportHttp extends HttpTransport implements WalkTransport, HEAD, ObjectId.fromString(line)); refs.put(r.getName(), r); } - } finally { - br.close(); } break; } @@ -438,8 +433,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport, final String service = SVC_RECEIVE_PACK; try { final HttpConnection c = connect(service); - final InputStream in = openInputStream(c); - try { + try (InputStream in = openInputStream(c)) { if (isSmartHttp(c, service)) { return smartPush(service, c, in); } else if (!useSmartHttp) { @@ -450,8 +444,6 @@ public class TransportHttp extends HttpTransport implements WalkTransport, final String msg = JGitText.get().remoteDoesNotSupportSmartHTTPPush; throw new NotSupportedException(msg); } - } finally { - in.close(); } } catch (NotSupportedException err) { throw err; @@ -966,21 +958,16 @@ public class TransportHttp extends HttpTransport implements WalkTransport, @Override Collection<String> getPackNames() throws IOException { final Collection<String> packs = new ArrayList<>(); - try { - final BufferedReader br = openReader(INFO_PACKS); - try { - for (;;) { - final String s = br.readLine(); - if (s == null || s.length() == 0) - break; - if (!s.startsWith("P pack-") || !s.endsWith(".pack")) //$NON-NLS-1$ //$NON-NLS-2$ - throw invalidAdvertisement(s); - packs.add(s.substring(2)); - } - return packs; - } finally { - br.close(); + try (BufferedReader br = openReader(INFO_PACKS)) { + for (;;) { + final String s = br.readLine(); + if (s == null || s.length() == 0) + break; + if (!s.startsWith("P pack-") || !s.endsWith(".pack")) //$NON-NLS-1$ //$NON-NLS-2$ + throw invalidAdvertisement(s); + packs.add(s.substring(2)); } + return packs; } catch (FileNotFoundException err) { return packs; } @@ -1165,10 +1152,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport, // Try to compress the content, but only if that is smaller. TemporaryBuffer buf = new TemporaryBuffer.Heap( http.getPostBuffer()); - try { - GZIPOutputStream gzip = new GZIPOutputStream(buf); + try (GZIPOutputStream gzip = new GZIPOutputStream(buf)) { out.writeTo(gzip, null); - gzip.close(); if (out.length() < buf.length()) buf = out; } catch (IOException err) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java index 20eb898fbe..4f1880b7ef 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java @@ -172,7 +172,9 @@ class TransportLocal extends Transport implements PackTransport { private Repository openRepo() throws TransportException { try { - return new RepositoryBuilder().setGitDir(remoteGitDir).build(); + return new RepositoryBuilder() + .setFS(local != null ? local.getFS() : FS.DETECTED) + .setGitDir(remoteGitDir).build(); } catch (IOException err) { throw new TransportException(uri, JGitText.get().notAGitDirectory); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java index 306c6c452c..09cd67b515 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java @@ -427,13 +427,8 @@ public class TransportSftp extends SshTransport implements WalkTransport { private Ref readRef(final TreeMap<String, Ref> avail, final String path, final String name) throws TransportException { final String line; - try { - final BufferedReader br = openReader(path); - try { - line = br.readLine(); - } finally { - br.close(); - } + try (BufferedReader br = openReader(path)) { + line = br.readLine(); } catch (FileNotFoundException noRef) { return null; } catch (IOException err) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java index 822d47c7c2..0209be1f73 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -773,6 +773,7 @@ public class UploadPack { boolean sendPack = false; // If it's a non-bidi request, we need to read the entire request before // writing a response. Buffer the response until then. + PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator(); try { if (biDirectionalPipe) sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut)); @@ -781,12 +782,15 @@ public class UploadPack { else advertised = refIdSet(getAdvertisedOrDefaultRefs().values()); + long negotiateStart = System.currentTimeMillis(); + accumulator.advertised = advertised.size(); recvWants(); if (wantIds.isEmpty()) { preUploadHook.onBeginNegotiateRound(this, wantIds, 0); preUploadHook.onEndNegotiateRound(this, wantIds, 0, 0, false); return; } + accumulator.wants = wantIds.size(); if (options.contains(OPTION_MULTI_ACK_DETAILED)) { multiAck = MultiAck.DETAILED; @@ -802,7 +806,10 @@ public class UploadPack { processShallow(); if (!clientShallowCommits.isEmpty()) walk.assumeShallow(clientShallowCommits); - sendPack = negotiate(); + sendPack = negotiate(accumulator); + accumulator.timeNegotiating += System.currentTimeMillis() + - negotiateStart; + if (sendPack && !biDirectionalPipe) { // Ensure the request was fully consumed. Any remaining input must // be a protocol error. If we aren't at EOF the implementation is broken. @@ -849,7 +856,7 @@ public class UploadPack { } if (sendPack) - sendPack(); + sendPack(accumulator); } private static Set<ObjectId> refIdSet(Collection<Ref> refs) { @@ -1093,7 +1100,8 @@ public class UploadPack { return UserAgent.getAgent(options, userAgent); } - private boolean negotiate() throws IOException { + private boolean negotiate(PackStatistics.Accumulator accumulator) + throws IOException { okToGiveUp = Boolean.FALSE; ObjectId last = ObjectId.zeroId(); @@ -1127,7 +1135,7 @@ public class UploadPack { } else if (line.startsWith("have ") && line.length() == 45) { //$NON-NLS-1$ peerHas.add(ObjectId.fromString(line.substring(5))); - + accumulator.haves++; } else if (line.equals("done")) { //$NON-NLS-1$ last = processHaveLines(peerHas, last); @@ -1485,12 +1493,13 @@ public class UploadPack { return false; } - private void sendPack() throws IOException { + private void sendPack(PackStatistics.Accumulator accumulator) + throws IOException { final boolean sideband = options.contains(OPTION_SIDE_BAND) || options.contains(OPTION_SIDE_BAND_64K); if (sideband) { try { - sendPack(true); + sendPack(true, accumulator); } catch (ServiceMayNotContinueException noPack) { // This was already reported on (below). throw noPack; @@ -1511,7 +1520,7 @@ public class UploadPack { throw err; } } else { - sendPack(false); + sendPack(false, accumulator); } } @@ -1532,7 +1541,8 @@ public class UploadPack { } @SuppressWarnings("deprecation") - private void sendPack(final boolean sideband) throws IOException { + private void sendPack(final boolean sideband, + PackStatistics.Accumulator accumulator) throws IOException { ProgressMonitor pm = NullProgressMonitor.INSTANCE; OutputStream packOut = rawOut; @@ -1573,7 +1583,8 @@ public class UploadPack { PackConfig cfg = packConfig; if (cfg == null) cfg = new PackConfig(db); - final PackWriter pw = new PackWriter(cfg, walk.getObjectReader()); + final PackWriter pw = new PackWriter(cfg, walk.getObjectReader(), + accumulator); try { pw.setIndexDisabled(true); pw.setUseCachedPacks(true); @@ -1617,6 +1628,10 @@ public class UploadPack { if (options.contains(OPTION_INCLUDE_TAG) && refs != null) { for (Ref ref : refs.values()) { ObjectId objectId = ref.getObjectId(); + if (objectId == null) { + // skip unborn branch + continue; + } // If the object was already requested, skip it. if (wantAll.isEmpty()) { @@ -1632,12 +1647,13 @@ public class UploadPack { ref = db.peel(ref); ObjectId peeledId = ref.getPeeledObjectId(); - if (peeledId == null) + objectId = ref.getObjectId(); + if (peeledId == null || objectId == null) continue; - objectId = ref.getObjectId(); - if (pw.willInclude(peeledId) && !pw.willInclude(objectId)) + if (pw.willInclude(peeledId) && !pw.willInclude(objectId)) { pw.addObject(rw.parseAny(objectId)); + } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java index a0ad2f3662..965be509cd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java @@ -264,11 +264,8 @@ abstract class WalkRemoteObjectDatabase { * failed, possibly due to permissions or remote disk full, etc. */ void writeFile(final String path, final byte[] data) throws IOException { - final OutputStream os = writeFile(path, null, null); - try { + try (OutputStream os = writeFile(path, null, null)) { os.write(data); - } finally { - os.close(); } } @@ -394,8 +391,7 @@ abstract class WalkRemoteObjectDatabase { */ Collection<WalkRemoteObjectDatabase> readAlternates(final String listPath) throws IOException { - final BufferedReader br = openReader(listPath); - try { + try (BufferedReader br = openReader(listPath)) { final Collection<WalkRemoteObjectDatabase> alts = new ArrayList<>(); for (;;) { String line = br.readLine(); @@ -406,8 +402,6 @@ abstract class WalkRemoteObjectDatabase { alts.add(openAlternate(line)); } return alts; - } finally { - br.close(); } } @@ -422,14 +416,8 @@ abstract class WalkRemoteObjectDatabase { */ protected void readPackedRefs(final Map<String, Ref> avail) throws TransportException { - try { - final BufferedReader br = openReader(ROOT_DIR - + Constants.PACKED_REFS); - try { - readPackedRefsImpl(avail, br); - } finally { - br.close(); - } + try (BufferedReader br = openReader(ROOT_DIR + Constants.PACKED_REFS)) { + readPackedRefsImpl(avail, br); } catch (FileNotFoundException notPacked) { // Perhaps it wasn't worthwhile, or is just an older repository. } catch (IOException e) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java index 3e06f046bf..5560f7750c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java @@ -187,7 +187,7 @@ public class NameConflictTreeWalk extends TreeWalk { // t.matches = minRef; } else if (fastMinHasMatch && isTree(t) && !isTree(minRef) - && nameEqual(t, minRef)) { + && !isGitlink(minRef) && nameEqual(t, minRef)) { // The minimum is a file (non-tree) but the next entry // of this iterator is a tree whose name matches our file. // This is a classic D/F conflict and commonly occurs like @@ -218,6 +218,10 @@ public class NameConflictTreeWalk extends TreeWalk { return a.pathCompare(b, TREE_MODE) == 0; } + private boolean isGitlink(AbstractTreeIterator p) { + return FileMode.GITLINK.equals(p.mode); + } + private static boolean isTree(final AbstractTreeIterator p) { return FileMode.TREE.equals(p.mode); } @@ -306,8 +310,9 @@ public class NameConflictTreeWalk extends TreeWalk { if (t.matches == minRef) t.matches = treeMatch; - if (dfConflict == null) + if (dfConflict == null && !isGitlink(minRef)) { dfConflict = treeMatch; + } return treeMatch; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java index a91ad592cf..8872689d36 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java @@ -1424,7 +1424,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { /** * Inspect config and attributes to return a filtercommand applicable for - * the current path + * the current path, but without expanding %f occurences * * @param filterCommandType * which type of filterCommand should be executed. E.g. "clean", diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index 8d02f90ed1..68cc7cb580 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -60,6 +60,8 @@ import java.text.MessageFormat; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; import org.eclipse.jgit.api.errors.FilterFailedException; import org.eclipse.jgit.attributes.AttributesNode; @@ -474,7 +476,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { while (command.run() != -1) { // loop as long as command.run() tells there is work to do } - return buffer.openInputStream(); + return buffer.openInputStreamWithAutoDestroy(); } FS fs = repository.getFS(); ProcessBuilder filterProcessBuilder = fs.runInShell(filterCommand, @@ -497,7 +499,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { RawParseUtils.decode(result.getStderr() .toByteArray(MAX_EXCEPTION_TEXT_SIZE)))); } - return result.getStdout().openInputStream(); + return result.getStdout().openInputStreamWithAutoDestroy(); } return in; } @@ -661,54 +663,60 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { * a relevant ignore rule file exists but cannot be read. */ protected boolean isEntryIgnored(final int pLen) throws IOException { - return isEntryIgnored(pLen, mode, false); + return isEntryIgnored(pLen, mode); } /** - * Determine if the entry path is ignored by an ignore rule. Consider - * possible rule negation from child iterator. + * Determine if the entry path is ignored by an ignore rule. * * @param pLen * the length of the path in the path buffer. * @param fileMode * the original iterator file mode - * @param negatePrevious - * true if the previous matching iterator rule was negation * @return true if the entry is ignored by an ignore rule. * @throws IOException * a relevant ignore rule file exists but cannot be read. */ - private boolean isEntryIgnored(final int pLen, int fileMode, - boolean negatePrevious) + private boolean isEntryIgnored(final int pLen, int fileMode) throws IOException { + // The ignore code wants path to start with a '/' if possible. + // If we have the '/' in our path buffer because we are inside + // a sub-directory include it in the range we convert to string. + // + final int pOff = 0 < pathOffset ? pathOffset - 1 : pathOffset; + String pathRel = TreeWalk.pathOf(this.path, pOff, pLen); + String parentRel = getParentPath(pathRel); + + // CGit is processing .gitignore files by starting at the root of the + // repository and then recursing into subdirectories. With this + // approach, top-level ignored directories will be processed first which + // allows to skip entire subtrees and further .gitignore-file processing + // within these subtrees. + // + // We will follow the same approach by marking directories as "ignored" + // here. This allows to have a simplified FastIgnore.checkIgnore() + // implementation (both in terms of code and computational complexity): + // + // Without the "ignored" flag, we would have to apply the ignore-check + // to a path and all of its parents always(!), to determine whether a + // path is ignored directly or by one of its parent directories; with + // the "ignored" flag, we know at this point that the parent directory + // is definitely not ignored, thus the path can only become ignored if + // there is a rule matching the path itself. + if (isDirectoryIgnored(parentRel)) { + return true; + } + IgnoreNode rules = getIgnoreNode(); - if (rules != null) { - // The ignore code wants path to start with a '/' if possible. - // If we have the '/' in our path buffer because we are inside - // a subdirectory include it in the range we convert to string. - // - int pOff = pathOffset; - if (0 < pOff) - pOff--; - String p = TreeWalk.pathOf(path, pOff, pLen); - switch (rules.isIgnored(p, FileMode.TREE.equals(fileMode), - negatePrevious)) { - case IGNORED: - return true; - case NOT_IGNORED: - return false; - case CHECK_PARENT: - negatePrevious = false; - break; - case CHECK_PARENT_NEGATE_FIRST_MATCH: - negatePrevious = true; - break; - } + final Boolean ignored = rules != null + ? rules.checkIgnored(pathRel, FileMode.TREE.equals(fileMode)) + : null; + if (ignored != null) { + return ignored.booleanValue(); } - if (parent instanceof WorkingTreeIterator) - return ((WorkingTreeIterator) parent).isEntryIgnored(pLen, fileMode, - negatePrevious); - return false; + return parent instanceof WorkingTreeIterator + && ((WorkingTreeIterator) parent).isEntryIgnored(pLen, + fileMode); } private IgnoreNode getIgnoreNode() throws IOException { @@ -952,7 +960,19 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } return false; case DIFFER_BY_METADATA: - if (mode == FileMode.SYMLINK.getBits()) + if (mode == FileMode.TREE.getBits() + && entry.getFileMode().equals(FileMode.GITLINK)) { + byte[] idBuffer = idBuffer(); + int idOffset = idOffset(); + if (entry.getObjectId().compareTo(idBuffer, idOffset) == 0) { + return true; + } else if (ObjectId.zeroId().compareTo(idBuffer, + idOffset) == 0) { + return new File(repository.getWorkTree(), + entry.getPathString()).list().length > 0; + } + return false; + } else if (mode == FileMode.SYMLINK.getBits()) return contentCheck(entry, reader); return true; default: @@ -1360,6 +1380,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { /** Position of the matching {@link DirCacheIterator}. */ int dirCacheTree; + final Map<String, Boolean> directoryToIgnored = new HashMap<>(); + IteratorState(WorkingTreeOptions options) { this.options = options; this.nameEncoder = Constants.CHARSET.newEncoder(); @@ -1436,4 +1458,67 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } return eolStreamTypeHolder.get(); } + + private boolean isDirectoryIgnored(String pathRel) throws IOException { + final int pOff = 0 < pathOffset ? pathOffset - 1 : pathOffset; + final String base = TreeWalk.pathOf(this.path, 0, pOff); + final String pathAbs = concatPath(base, pathRel); + return isDirectoryIgnored(pathRel, pathAbs); + } + + private boolean isDirectoryIgnored(String pathRel, String pathAbs) + throws IOException { + assert pathRel.length() == 0 || (pathRel.charAt(0) != '/' + && pathRel.charAt(pathRel.length() - 1) != '/'); + assert pathAbs.length() == 0 || (pathAbs.charAt(0) != '/' + && pathAbs.charAt(pathAbs.length() - 1) != '/'); + assert pathAbs.endsWith(pathRel); + + Boolean ignored = state.directoryToIgnored.get(pathAbs); + if (ignored != null) { + return ignored.booleanValue(); + } + + final String parentRel = getParentPath(pathRel); + if (parentRel != null && isDirectoryIgnored(parentRel)) { + state.directoryToIgnored.put(pathAbs, Boolean.TRUE); + return true; + } + + final IgnoreNode node = getIgnoreNode(); + for (String p = pathRel; node != null + && !"".equals(p); p = getParentPath(p)) { //$NON-NLS-1$ + ignored = node.checkIgnored(p, true); + if (ignored != null) { + state.directoryToIgnored.put(pathAbs, ignored); + return ignored.booleanValue(); + } + } + + if (!(this.parent instanceof WorkingTreeIterator)) { + state.directoryToIgnored.put(pathAbs, Boolean.FALSE); + return false; + } + + final WorkingTreeIterator wtParent = (WorkingTreeIterator) this.parent; + final String parentRelPath = concatPath( + TreeWalk.pathOf(this.path, wtParent.pathOffset, pathOffset - 1), + pathRel); + assert concatPath(TreeWalk.pathOf(wtParent.path, 0, + Math.max(0, wtParent.pathOffset - 1)), parentRelPath) + .equals(pathAbs); + return wtParent.isDirectoryIgnored(parentRelPath, pathAbs); + } + + private static String getParentPath(String path) { + final int slashIndex = path.lastIndexOf('/', path.length() - 2); + if (slashIndex > 0) { + return path.substring(path.charAt(0) == '/' ? 1 : 0, slashIndex); + } + return path.length() > 0 ? "" : null; //$NON-NLS-1$ + } + + private static String concatPath(String p1, String p2) { + return p1 + (p1.length() > 0 && p2.length() > 0 ? "/" : "") + p2; //$NON-NLS-1$ //$NON-NLS-2$ + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java index b43a7612de..edcb9d7a6a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -1227,14 +1227,11 @@ public abstract class FS { */ public ExecutionResult execute(ProcessBuilder pb, InputStream in) throws IOException, InterruptedException { - TemporaryBuffer stdout = new TemporaryBuffer.LocalFile(null); - TemporaryBuffer stderr = new TemporaryBuffer.Heap(1024, 1024 * 1024); - try { + try (TemporaryBuffer stdout = new TemporaryBuffer.LocalFile(null); + TemporaryBuffer stderr = new TemporaryBuffer.Heap(1024, + 1024 * 1024)) { int rc = runProcess(pb, stdout, stderr, in); return new ExecutionResult(stdout, stderr, rc); - } finally { - stdout.close(); - stderr.close(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java new file mode 100644 index 0000000000..6d60ef3f4d --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2018, Markus Duft <markus.duft@ssi-schaefer.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.text.MessageFormat; +import java.util.concurrent.Callable; + +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.attributes.Attribute; +import org.eclipse.jgit.attributes.Attributes; +import org.eclipse.jgit.hooks.PrePushHook; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathFilter; + +/** + * Represents an optionally present LFS support implementation + * + * @since 4.11 + */ +public class LfsFactory { + + private static LfsFactory instance = new LfsFactory(); + + /** + * Constructor + */ + protected LfsFactory() { + } + + /** + * @return the current LFS implementation + */ + public static LfsFactory getInstance() { + return instance; + } + + /** + * @param instance + * register a {@link LfsFactory} instance as the + * {@link LfsFactory} implementation to use. + */ + public static void setInstance(LfsFactory instance) { + LfsFactory.instance = instance; + } + + /** + * @return whether LFS support is available + */ + public boolean isAvailable() { + return false; + } + + /** + * Apply clean filtering to the given stream, writing the file content to + * the LFS storage if required and returning a stream to the LFS pointer + * instead. + * + * @param db + * the repository + * @param input + * the original input + * @param length + * the expected input stream length + * @param attribute + * the attribute used to check for LFS enablement (i.e. "merge", + * "diff", "filter" from .gitattributes). + * @return a stream to the content that should be written to the object + * store along with the expected length of the stream. the original + * stream is not applicable. + * @throws IOException + * in case of an error + */ + public LfsInputStream applyCleanFilter(Repository db, + InputStream input, long length, Attribute attribute) + throws IOException { + return new LfsInputStream(input, length); + } + + /** + * Apply smudge filtering to a given loader, potentially redirecting it to a + * LFS blob which is downloaded on demand. + * + * @param db + * the repository + * @param loader + * the loader for the blob + * @param attribute + * the attribute used to check for LFS enablement (i.e. "merge", + * "diff", "filter" from .gitattributes). + * @return a loader for the actual data of a blob, or the original loader in + * case LFS is not applicable. + * @throws IOException + */ + public ObjectLoader applySmudgeFilter(Repository db, + ObjectLoader loader, Attribute attribute) throws IOException { + return loader; + } + + /** + * Retrieve a pre-push hook to be applied. + * + * @param repo + * the {@link Repository} the hook is applied to. + * @param outputStream + * @return a {@link PrePushHook} implementation or <code>null</code> + */ + public @Nullable PrePushHook getPrePushHook(Repository repo, + PrintStream outputStream) { + return null; + } + + /** + * Retrieve an {@link LfsInstallCommand} which can be used to enable LFS + * support (if available) either per repository or for the user. + * + * @return a command to install LFS support. + */ + public @Nullable LfsInstallCommand getInstallCommand() { + return null; + } + + /** + * @param db + * the repository to check + * @return whether LFS is enabled for the given repository locally or + * globally. + */ + public boolean isEnabled(Repository db) { + return false; + } + + /** + * @param db + * the repository + * @param path + * the path to find attributes for + * @return the {@link Attributes} for the given path. + * @throws IOException + * in case of an error + */ + public static Attributes getAttributesForPath(Repository db, String path) + throws IOException { + try (TreeWalk walk = new TreeWalk(db)) { + walk.addTree(new FileTreeIterator(db)); + PathFilter f = PathFilter.create(path); + walk.setFilter(f); + walk.setRecursive(false); + Attributes attr = null; + while (walk.next()) { + if (f.isDone(walk)) { + attr = walk.getAttributes(); + break; + } else if (walk.isSubtree()) { + walk.enterSubtree(); + } + } + if (attr == null) { + throw new IOException(MessageFormat + .format(JGitText.get().noPathAttributesFound, path)); + } + + return attr; + } + } + + /** + * Get attributes for given path and commit + * + * @param db + * the repository + * @param path + * the path to find attributes for + * @param commit + * the commit to inspect. + * @return the {@link Attributes} for the given path. + * @throws IOException + * in case of an error + */ + public static Attributes getAttributesForPath(Repository db, String path, + RevCommit commit) throws IOException { + if (commit == null) { + return getAttributesForPath(db, path); + } + + try (TreeWalk walk = TreeWalk.forPath(db, path, commit.getTree())) { + Attributes attr = walk == null ? null : walk.getAttributes(); + if (attr == null) { + throw new IOException(MessageFormat + .format(JGitText.get().noPathAttributesFound, path)); + } + + return attr; + } + } + + /** + * Encapsulate a potentially exchanged {@link InputStream} along with the + * expected stream content length. + */ + public static final class LfsInputStream extends InputStream { + /** + * The actual stream. + */ + private InputStream stream; + + /** + * The expected stream content length. + */ + private long length; + + /** + * Create a new wrapper around a certain stream + * + * @param stream + * the stream to wrap. the stream will be closed on + * {@link #close()}. + * @param length + * the expected length of the stream + */ + public LfsInputStream(InputStream stream, long length) { + this.stream = stream; + this.length = length; + } + + /** + * Create a new wrapper around a temporary buffer. + * + * @param buffer + * the buffer to initialize stream and length from. The + * buffer will be destroyed on {@link #close()} + * @throws IOException + * in case of an error opening the stream to the buffer. + */ + public LfsInputStream(TemporaryBuffer buffer) throws IOException { + this.stream = buffer.openInputStreamWithAutoDestroy(); + this.length = buffer.length(); + } + + @Override + public void close() throws IOException { + stream.close(); + } + + @Override + public int read() throws IOException { + return stream.read(); + } + + /** + * @return the length of the stream + */ + public long getLength() { + return length; + } + } + + /** + * A command to enable LFS. Optionally set a {@link Repository} to enable + * locally on the repository only. + */ + public interface LfsInstallCommand extends Callable<Void> { + /** + * @param repo + * the repository to enable support for. + * @return The {@link LfsInstallCommand} for chaining. + */ + public LfsInstallCommand setRepository(Repository repo); + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java index 31abb7c173..dd933a0294 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java @@ -314,6 +314,26 @@ public abstract class TemporaryBuffer extends OutputStream { } /** + * Same as {@link #openInputStream()} but handling destruction of any + * associated resources automatically when closing the returned stream. + * + * @return an InputStream which will automatically destroy any associated + * temporary file on {@link #close()} + * @throws IOException + * in case of an error. + * @since 4.11 + */ + public InputStream openInputStreamWithAutoDestroy() throws IOException { + return new BlockInputStream() { + @Override + public void close() throws IOException { + super.close(); + destroy(); + } + }; + } + + /** * Reset this buffer for reuse, purging all buffered content. */ public void reset() { @@ -506,6 +526,20 @@ public abstract class TemporaryBuffer extends OutputStream { } @Override + public InputStream openInputStreamWithAutoDestroy() throws IOException { + if (onDiskFile == null) { + return super.openInputStreamWithAutoDestroy(); + } + return new FileInputStream(onDiskFile) { + @Override + public void close() throws IOException { + super.close(); + destroy(); + } + }; + } + + @Override public void destroy() { super.destroy(); @@ -51,7 +51,7 @@ <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> <packaging>pom</packaging> - <version>4.10.1-SNAPSHOT</version> + <version>4.11.2-SNAPSHOT</version> <name>JGit - Parent</name> <url>${jgit-url}</url> @@ -89,6 +89,9 @@ <name>Dave Borowitz</name> </developer> <developer> + <name>David Pursehouse</name> + </developer> + <developer> <name>Gunnar Wagenknecht</name> </developer> <developer> @@ -118,6 +121,9 @@ <developer> <name>Stefan Lay</name> </developer> + <developer> + <name>Thomas Wolf</name> + </developer> </developers> <mailingLists> @@ -191,13 +197,14 @@ <maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format> <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest> - <jgit-last-release-version>4.9.2.201712150930-r</jgit-last-release-version> + <jgit-last-release-version>4.11.0.201803080745-r</jgit-last-release-version> <jsch-version>0.1.54</jsch-version> + <jzlib-version>1.1.1</jzlib-version> <javaewah-version>1.1.6</javaewah-version> <junit-version>4.12</junit-version> <test-fork-count>1C</test-fork-count> <args4j-version>2.33</args4j-version> - <commons-compress-version>1.6</commons-compress-version> + <commons-compress-version>1.15</commons-compress-version> <osgi-core-version>4.3.1</osgi-core-version> <servlet-api-version>3.1.0</servlet-api-version> <jetty-version>9.4.8.v20171121</jetty-version> @@ -207,9 +214,9 @@ <slf4j-version>1.7.2</slf4j-version> <log4j-version>1.2.15</log4j-version> <maven-javadoc-plugin-version>3.0.0</maven-javadoc-plugin-version> - <tycho-extras-version>1.0.0</tycho-extras-version> - <gson-version>2.2.4</gson-version> - <spotbugs-maven-plugin-version>3.1.0</spotbugs-maven-plugin-version> + <tycho-extras-version>1.1.0</tycho-extras-version> + <gson-version>2.8.2</gson-version> + <spotbugs-maven-plugin-version>3.1.2</spotbugs-maven-plugin-version> <maven-surefire-report-plugin-version>2.20.1</maven-surefire-report-plugin-version> <!-- Properties to enable jacoco code coverage analysis --> @@ -311,7 +318,7 @@ <dependency> <groupId>com.google.errorprone</groupId> <artifactId>error_prone_core</artifactId> - <version>2.1.3</version> + <version>2.2.0</version> </dependency> </dependencies> </plugin> @@ -624,6 +631,12 @@ </dependency> <dependency> + <groupId>com.jcraft</groupId> + <artifactId>jzlib</artifactId> + <version>${jzlib-version}</version> + </dependency> + + <dependency> <groupId>com.googlecode.javaewah</groupId> <artifactId>JavaEWAH</artifactId> <version>${javaewah-version}</version> @@ -654,6 +667,13 @@ </dependency> <dependency> + <groupId>org.tukaani</groupId> + <artifactId>xz</artifactId> + <version>1.6</version> + <optional>true</optional> + </dependency> + + <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> <version>${jetty-version}</version> |