diff options
Diffstat (limited to 'org.eclipse.jgit')
60 files changed, 1304 insertions, 545 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index 842fc873d4..f50ea9a6d5 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -20,17 +20,12 @@ </message_arguments> </filter> </resource> - <resource path="src/org/eclipse/jgit/errors/PackInvalidException.java" type="org.eclipse.jgit.errors.PackInvalidException"> - <filter id="1142947843"> - <message_arguments> - <message_argument value="4.5.7"/> - <message_argument value="PackInvalidException(File, Throwable)"/> - </message_arguments> - </filter> - <filter id="1142947843"> + <resource path="src/org/eclipse/jgit/lib/AnyObjectId.java" type="org.eclipse.jgit.lib.AnyObjectId"> + <filter id="1141899266"> <message_arguments> - <message_argument value="4.5.7"/> - <message_argument value="PackInvalidException(String, Throwable)"/> + <message_argument value="5.4"/> + <message_argument value="5.5"/> + <message_argument value="isEqual(AnyObjectId, AnyObjectId)"/> </message_arguments> </filter> </resource> @@ -54,136 +49,6 @@ </message_arguments> </filter> </resource> - <resource path="src/org/eclipse/jgit/lib/Ref.java" type="org.eclipse.jgit.lib.Ref"> - <filter id="403767336"> - <message_arguments> - <message_argument value="org.eclipse.jgit.lib.Ref"/> - <message_argument value="UNDEFINED_UPDATE_INDEX"/> - </message_arguments> - </filter> - </resource> - <resource path="src/org/eclipse/jgit/lib/Repository.java" type="org.eclipse.jgit.lib.Repository"> - <filter id="336695337"> - <message_arguments> - <message_argument value="org.eclipse.jgit.lib.Repository"/> - <message_argument value="getIdentifier()"/> - </message_arguments> - </filter> - </resource> - <resource path="src/org/eclipse/jgit/revwalk/ObjectWalk.java" type="org.eclipse.jgit.revwalk.ObjectWalk"> - <filter id="336658481"> - <message_arguments> - <message_argument value="org.eclipse.jgit.revwalk.ObjectWalk"/> - <message_argument value="SIMPLE_VISITATION_POLICY"/> - </message_arguments> - </filter> - </resource> - <resource path="src/org/eclipse/jgit/storage/pack/PackConfig.java" type="org.eclipse.jgit.storage.pack.PackConfig"> - <filter id="336658481"> - <message_arguments> - <message_argument value="org.eclipse.jgit.storage.pack.PackConfig"/> - <message_argument value="DEFAULT_MINSIZE_PREVENT_RACY_PACK"/> - </message_arguments> - </filter> - <filter id="336658481"> - <message_arguments> - <message_argument value="org.eclipse.jgit.storage.pack.PackConfig"/> - <message_argument value="DEFAULT_WAIT_PREVENT_RACY_PACK"/> - </message_arguments> - </filter> - <filter id="1142947843"> - <message_arguments> - <message_argument value="5.1.8"/> - <message_argument value="DEFAULT_MINSIZE_PREVENT_RACY_PACK"/> - </message_arguments> - </filter> - <filter id="1142947843"> - <message_arguments> - <message_argument value="5.1.8"/> - <message_argument value="DEFAULT_WAIT_PREVENT_RACY_PACK"/> - </message_arguments> - </filter> - <filter id="1142947843"> - <message_arguments> - <message_argument value="5.1.8"/> - <message_argument value="doWaitPreventRacyPack(long)"/> - </message_arguments> - </filter> - <filter id="1142947843"> - <message_arguments> - <message_argument value="5.1.8"/> - <message_argument value="getMinSizePreventRacyPack()"/> - </message_arguments> - </filter> - <filter id="1142947843"> - <message_arguments> - <message_argument value="5.1.8"/> - <message_argument value="isWaitPreventRacyPack()"/> - </message_arguments> - </filter> - <filter id="1142947843"> - <message_arguments> - <message_argument value="5.1.8"/> - <message_argument value="setMinSizePreventRacyPack(long)"/> - </message_arguments> - </filter> - <filter id="1142947843"> - <message_arguments> - <message_argument value="5.1.8"/> - <message_argument value="setWaitPreventRacyPack(boolean)"/> - </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="treesTraversed"/> - </message_arguments> - </filter> - </resource> - <resource path="src/org/eclipse/jgit/transport/HttpConfig.java" type="org.eclipse.jgit.transport.HttpConfig"> - <filter id="336658481"> - <message_arguments> - <message_argument value="org.eclipse.jgit.transport.HttpConfig"/> - <message_argument value="COOKIE_FILE_CACHE_LIMIT_KEY"/> - </message_arguments> - </filter> - <filter id="336658481"> - <message_arguments> - <message_argument value="org.eclipse.jgit.transport.HttpConfig"/> - <message_argument value="COOKIE_FILE_KEY"/> - </message_arguments> - </filter> - <filter id="336658481"> - <message_arguments> - <message_argument value="org.eclipse.jgit.transport.HttpConfig"/> - <message_argument value="SAVE_COOKIES_KEY"/> - </message_arguments> - </filter> - </resource> - <resource path="src/org/eclipse/jgit/transport/Transport.java" type="org.eclipse.jgit.transport.Transport"> - <filter id="421654647"> - <message_arguments> - <message_argument value="org.eclipse.jgit.transport.Transport"/> - <message_argument value="getFilterBlobLimit()"/> - </message_arguments> - </filter> - <filter id="421654647"> - <message_arguments> - <message_argument value="org.eclipse.jgit.transport.Transport"/> - <message_argument value="setFilterBlobLimit(long)"/> - </message_arguments> - </filter> - </resource> - <resource path="src/org/eclipse/jgit/transport/UploadPack.java" type="org.eclipse.jgit.transport.UploadPack"> - <filter id="421654647"> - <message_arguments> - <message_argument value="org.eclipse.jgit.transport.UploadPack"/> - <message_argument value="getFilterBlobLimit()"/> - </message_arguments> - </filter> - </resource> <resource path="src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java" type="org.eclipse.jgit.treewalk.WorkingTreeIterator"> <filter id="1142947843"> <message_arguments> @@ -207,6 +72,12 @@ </filter> </resource> <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS"> + <filter id="338792546"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.FS"/> + <message_argument value="getFsTimerResolution(Path)"/> + </message_arguments> + </filter> <filter id="1142947843"> <message_arguments> <message_argument value="5.1.9"/> @@ -254,31 +125,12 @@ </message_arguments> </filter> </resource> - <resource path="src/org/eclipse/jgit/util/FileUtils.java" type="org.eclipse.jgit.util.FileUtils"> - <filter id="1142947843"> - <message_arguments> - <message_argument value="5.1.8"/> - <message_argument value="touch(Path)"/> - </message_arguments> - </filter> - </resource> - <resource path="src/org/eclipse/jgit/util/HttpSupport.java" type="org.eclipse.jgit.util.HttpSupport"> - <filter id="336658481"> - <message_arguments> - <message_argument value="org.eclipse.jgit.util.HttpSupport"/> - <message_argument value="HDR_COOKIE"/> - </message_arguments> - </filter> - <filter id="336658481"> - <message_arguments> - <message_argument value="org.eclipse.jgit.util.HttpSupport"/> - <message_argument value="HDR_SET_COOKIE"/> - </message_arguments> - </filter> - <filter id="336658481"> + <resource path="src/org/eclipse/jgit/util/References.java" type="org.eclipse.jgit.util.References"> + <filter id="1108344834"> <message_arguments> - <message_argument value="org.eclipse.jgit.util.HttpSupport"/> - <message_argument value="HDR_SET_COOKIE2"/> + <message_argument value="5.4"/> + <message_argument value="5.5"/> + <message_argument value="org.eclipse.jgit.util.References"/> </message_arguments> </filter> </resource> diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD index c7d9fde2fd..dcfeb1702a 100644 --- a/org.eclipse.jgit/BUILD +++ b/org.eclipse.jgit/BUILD @@ -16,10 +16,6 @@ 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 = [ diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index 5bfa344c48..1a8d43c185 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -1,14 +1,14 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 -Bundle-Name: %plugin_name +Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit Bundle-SymbolicName: org.eclipse.jgit -Bundle-Version: 5.4.4.qualifier +Bundle-Version: 5.5.1.qualifier Bundle-Localization: plugin -Bundle-Vendor: %provider_name +Bundle-Vendor: %Bundle-Vendor Bundle-ActivationPolicy: lazy -Export-Package: org.eclipse.jgit.annotations;version="5.4.4", - org.eclipse.jgit.api;version="5.4.4"; +Export-Package: org.eclipse.jgit.annotations;version="5.5.1", + org.eclipse.jgit.api;version="5.5.1"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff, @@ -22,53 +22,53 @@ Export-Package: org.eclipse.jgit.annotations;version="5.4.4", org.eclipse.jgit.submodule, org.eclipse.jgit.transport, org.eclipse.jgit.merge", - org.eclipse.jgit.api.errors;version="5.4.4";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors", - org.eclipse.jgit.attributes;version="5.4.4", - org.eclipse.jgit.blame;version="5.4.4"; + org.eclipse.jgit.api.errors;version="5.5.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors", + org.eclipse.jgit.attributes;version="5.5.1", + org.eclipse.jgit.blame;version="5.5.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff", - org.eclipse.jgit.diff;version="5.4.4"; + org.eclipse.jgit.diff;version="5.5.1"; 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="5.4.4"; + org.eclipse.jgit.dircache;version="5.5.1"; 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="5.4.4"; + org.eclipse.jgit.errors;version="5.5.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.internal.storage.pack, org.eclipse.jgit.transport, org.eclipse.jgit.dircache", - org.eclipse.jgit.events;version="5.4.4";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.fnmatch;version="5.4.4", - org.eclipse.jgit.gitrepo;version="5.4.4"; + org.eclipse.jgit.events;version="5.5.1";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.fnmatch;version="5.5.1", + org.eclipse.jgit.gitrepo;version="5.5.1"; 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="5.4.4";x-internal:=true, - org.eclipse.jgit.hooks;version="5.4.4";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.ignore;version="5.4.4", - org.eclipse.jgit.ignore.internal;version="5.4.4";x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal;version="5.4.4";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", - org.eclipse.jgit.internal.fsck;version="5.4.4";x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.ketch;version="5.4.4";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.revwalk;version="5.4.4";x-internal:=true, - org.eclipse.jgit.internal.storage.dfs;version="5.4.4"; + org.eclipse.jgit.gitrepo.internal;version="5.5.1";x-internal:=true, + org.eclipse.jgit.hooks;version="5.5.1";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.ignore;version="5.5.1", + org.eclipse.jgit.ignore.internal;version="5.5.1";x-friends:="org.eclipse.jgit.test", + org.eclipse.jgit.internal;version="5.5.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", + org.eclipse.jgit.internal.fsck;version="5.5.1";x-friends:="org.eclipse.jgit.test", + org.eclipse.jgit.internal.ketch;version="5.5.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.revwalk;version="5.5.1";x-internal:=true, + org.eclipse.jgit.internal.storage.dfs;version="5.5.1"; 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="5.4.4"; + org.eclipse.jgit.internal.storage.file;version="5.5.1"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.junit, org.eclipse.jgit.junit.http, @@ -77,19 +77,19 @@ Export-Package: org.eclipse.jgit.annotations;version="5.4.4", org.eclipse.jgit.pgm, org.eclipse.jgit.pgm.test, org.eclipse.jgit.ssh.apache", - org.eclipse.jgit.internal.storage.io;version="5.4.4";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.pack;version="5.4.4";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.reftable;version="5.4.4"; + org.eclipse.jgit.internal.storage.io;version="5.5.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.storage.pack;version="5.5.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.storage.reftable;version="5.5.1"; 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="5.4.4";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.submodule;version="5.4.4";x-internal:=true, - org.eclipse.jgit.internal.transport.http;version="5.4.4";x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.parser;version="5.4.4";x-friends:="org.eclipse.jgit.http.server,org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.ssh;version="5.4.4";x-friends:="org.eclipse.jgit.ssh.apache", - org.eclipse.jgit.lib;version="5.4.4"; + org.eclipse.jgit.internal.storage.reftree;version="5.5.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.submodule;version="5.5.1";x-internal:=true, + org.eclipse.jgit.internal.transport.http;version="5.5.1";x-friends:="org.eclipse.jgit.test", + org.eclipse.jgit.internal.transport.parser;version="5.5.1";x-friends:="org.eclipse.jgit.http.server,org.eclipse.jgit.test", + org.eclipse.jgit.internal.transport.ssh;version="5.5.1";x-friends:="org.eclipse.jgit.ssh.apache", + org.eclipse.jgit.lib;version="5.5.1"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util, @@ -99,33 +99,33 @@ Export-Package: org.eclipse.jgit.annotations;version="5.4.4", org.eclipse.jgit.treewalk, org.eclipse.jgit.transport, org.eclipse.jgit.submodule", - org.eclipse.jgit.lib.internal;version="5.4.4";x-internal:=true, - org.eclipse.jgit.merge;version="5.4.4"; + org.eclipse.jgit.lib.internal;version="5.5.1";x-internal:=true, + org.eclipse.jgit.merge;version="5.5.1"; 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="5.4.4", - org.eclipse.jgit.notes;version="5.4.4"; + org.eclipse.jgit.nls;version="5.5.1", + org.eclipse.jgit.notes;version="5.5.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.merge", - org.eclipse.jgit.patch;version="5.4.4";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff", - org.eclipse.jgit.revplot;version="5.4.4";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk", - org.eclipse.jgit.revwalk;version="5.4.4"; + org.eclipse.jgit.patch;version="5.5.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff", + org.eclipse.jgit.revplot;version="5.5.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk", + org.eclipse.jgit.revwalk;version="5.5.1"; 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="5.4.4";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util", - org.eclipse.jgit.storage.file;version="5.4.4";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util", - org.eclipse.jgit.storage.pack;version="5.4.4";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.submodule;version="5.4.4";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk", - org.eclipse.jgit.transport;version="5.4.4"; + org.eclipse.jgit.revwalk.filter;version="5.5.1";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util", + org.eclipse.jgit.storage.file;version="5.5.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util", + org.eclipse.jgit.storage.pack;version="5.5.1";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.submodule;version="5.5.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk", + org.eclipse.jgit.transport;version="5.5.1"; uses:="org.eclipse.jgit.transport.resolver, org.eclipse.jgit.revwalk, org.eclipse.jgit.internal.storage.pack, @@ -138,24 +138,24 @@ Export-Package: org.eclipse.jgit.annotations;version="5.4.4", org.eclipse.jgit.transport.http, org.eclipse.jgit.errors, org.eclipse.jgit.storage.pack", - org.eclipse.jgit.transport.http;version="5.4.4";uses:="javax.net.ssl", - org.eclipse.jgit.transport.resolver;version="5.4.4";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport", - org.eclipse.jgit.treewalk;version="5.4.4"; + org.eclipse.jgit.transport.http;version="5.5.1";uses:="javax.net.ssl", + org.eclipse.jgit.transport.resolver;version="5.5.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport", + org.eclipse.jgit.treewalk;version="5.5.1"; 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="5.4.4";uses:="org.eclipse.jgit.treewalk", - org.eclipse.jgit.util;version="5.4.4"; + org.eclipse.jgit.treewalk.filter;version="5.5.1";uses:="org.eclipse.jgit.treewalk", + org.eclipse.jgit.util;version="5.5.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.transport.http, org.eclipse.jgit.storage.file, org.ietf.jgss", - org.eclipse.jgit.util.io;version="5.4.4", - org.eclipse.jgit.util.sha1;version="5.4.4", - org.eclipse.jgit.util.time;version="5.4.4" + org.eclipse.jgit.util.io;version="5.5.1", + org.eclipse.jgit.util.sha1;version="5.5.1", + org.eclipse.jgit.util.time;version="5.5.1" 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 c3d522448e..d9e680a69a 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: 5.4.4.qualifier -Eclipse-SourceBundle: org.eclipse.jgit;version="5.4.4.qualifier";roots="." +Bundle-Version: 5.5.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit;version="5.5.1.qualifier";roots="." diff --git a/org.eclipse.jgit/plugin.properties b/org.eclipse.jgit/plugin.properties index 3ce6709690..3e132b01ca 100644 --- a/org.eclipse.jgit/plugin.properties +++ b/org.eclipse.jgit/plugin.properties @@ -1,2 +1,2 @@ -plugin_name=JGit Core -provider_name=Eclipse JGit +Bundle-Name=JGit Core +Bundle-Vendor=Eclipse JGit diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index 7576f3765a..bff6c7cd2f 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>5.4.4-SNAPSHOT</version> + <version>5.5.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit</artifactId> 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 049888272b..6b09faf0a1 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -72,6 +72,7 @@ cannotDownload=Cannot download {0} cannotEnterObjectsPath=Cannot enter {0}/objects: {1} cannotEnterPathFromParent=Cannot enter {0} from {1}: {2} cannotExecute=cannot execute: {0} +cannotFindMergeBaseUsingFirstParent=Cannot find merge bases using a first-parent walk. cannotGet=Cannot get {0} cannotGetObjectsPath=Cannot get {0}/{1}: {2} cannotListObjectsPath=Cannot ls {0}/{1}: {2} @@ -125,6 +126,7 @@ commandWasCalledInTheWrongState=Command {0} was called in the wrong state commitMessageNotSpecified=commit message not specified commitOnRepoWithoutHEADCurrentlyNotSupported=Commit on repo without HEAD currently not supported commitAmendOnInitialNotPossible=Amending is not possible on initial commit. +commitsHaveAlreadyBeenMarkedAsStart=Commits have already been marked as walk starts. compressingObjects=Compressing objects configSubsectionContainsNewline=config subsection name contains newline configSubsectionContainsNullByte=config subsection name contains byte 0x00 @@ -529,6 +531,8 @@ readFileStoreAttributesFailed=Reading FileStore attributes from user config fail readerIsRequired=Reader is required readingObjectsFromLocalRepositoryFailed=reading objects from local repository failed: {0} readLastModifiedFailed=Reading lastModified of {0} failed +readPipeIsNotAllowed=FS.readPipe() isn't allowed for command ''{0}''. Working directory: ''{1}''. +readPipeIsNotAllowedRequiredPermission=FS.readPipe() isn't allowed for command ''{0}''. Working directory: ''{1}''. Required permission: {2}. readTimedOut=Read timed out after {0} ms receivePackObjectTooLarge1=Object too large, rejecting the pack. Max object size limit is {0} bytes. receivePackObjectTooLarge2=Object too large ({0} bytes), rejecting the pack. Max object size limit is {1} bytes. @@ -544,6 +548,7 @@ refAlreadyExists=already exists refAlreadyExists1=Ref {0} already exists reflogEntryNotFound=Entry {0} not found in reflog for ''{1}'' refNotResolved=Ref {0} cannot be resolved +refTableRecordsMustIncrease=records must be increasing: last {0}, this {1} refUpdateReturnCodeWas=RefUpdate return code was: {0} remoteConfigHasNoURIAssociated=Remote config "{0}" has no URIs associated remoteDoesNotHaveSpec=Remote does not have {0} available for fetch. @@ -602,6 +607,7 @@ shortSkipOfBlock=Short skip of block. signingNotSupportedOnTag=Signing isn't supported on tag operations yet. similarityScoreMustBeWithinBounds=Similarity score must be between 0 and 100. skipMustBeNonNegative=skip must be >= 0 +skipNotAccessiblePath=The path ''{0}'' isn't accessible. Skip it. smartHTTPPushDisabled=smart HTTP push disabled sourceDestinationMustMatch=Source/Destination must match. sourceIsNotAWildcard=Source is not a wildcard. 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 c6f3c671a9..38e5444523 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java @@ -279,9 +279,13 @@ public class ApplyCommand extends GitCommand<ApplyResult> { } private boolean isNoNewlineAtEndOfFile(FileHeader fh) { - HunkHeader lastHunk = fh.getHunks().get(fh.getHunks().size() - 1); + List<? extends HunkHeader> hunks = fh.getHunks(); + if (hunks == null || hunks.isEmpty()) { + return false; + } + HunkHeader lastHunk = hunks.get(hunks.size() - 1); RawText lhrt = new RawText(lastHunk.getBuffer()); - return lhrt.getString(lhrt.size() - 1).equals( - "\\ No newline at end of file"); //$NON-NLS-1$ + return lhrt.getString(lhrt.size() - 1) + .equals("\\ No newline at end of file"); //$NON-NLS-1$ } } 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 b8fa74de79..f9a9baf919 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java @@ -459,8 +459,8 @@ public class MergeCommand extends GitCommand<MergeResult> { } /** - * Use values from the configuation if they have not been explicitly defined - * via the setters + * Use values from the configuration if they have not been explicitly + * defined via the setters */ private void fallBackToConfiguration() { MergeConfig config = MergeConfig.getConfigForCurrentBranch(repo); 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 22090f5b60..6bc2946078 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -1301,14 +1301,6 @@ public class DirCacheCheckout { JGitText.get().cannotDeleteFile, c)); removeEmptyParents(conflict); } - for (String r : removed) { - File file = new File(repo.getWorkTree(), r); - if (!file.delete()) - throw new CheckoutConflictException( - MessageFormat.format(JGitText.get().cannotDeleteFile, - file.getAbsolutePath())); - removeEmptyParents(file); - } } /** @@ -1473,6 +1465,9 @@ public class DirCacheCheckout { ObjectLoader ol = or.open(entry.getObjectId()); File f = new File(repo.getWorkTree(), entry.getPathString()); File parentDir = f.getParentFile(); + if (parentDir.isFile()) { + FileUtils.delete(parentDir); + } FileUtils.mkdirs(parentDir, true); FS fs = repo.getFS(); WorkingTreeOptions opt = repo.getConfig().get(WorkingTreeOptions.KEY); 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 60d1dab23f..6a7d22df9d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -133,6 +133,7 @@ public class JGitText extends TranslationBundle { /***/ public String cannotEnterObjectsPath; /***/ public String cannotEnterPathFromParent; /***/ public String cannotExecute; + /***/ public String cannotFindMergeBaseUsingFirstParent; /***/ public String cannotGet; /***/ public String cannotGetObjectsPath; /***/ public String cannotListObjectsPath; @@ -186,6 +187,7 @@ public class JGitText extends TranslationBundle { /***/ public String commitMessageNotSpecified; /***/ public String commitOnRepoWithoutHEADCurrentlyNotSupported; /***/ public String commitAmendOnInitialNotPossible; + /***/ public String commitsHaveAlreadyBeenMarkedAsStart; /***/ public String compressingObjects; /***/ public String configSubsectionContainsNewline; /***/ public String configSubsectionContainsNullByte; @@ -590,6 +592,8 @@ public class JGitText extends TranslationBundle { /***/ public String readerIsRequired; /***/ public String readingObjectsFromLocalRepositoryFailed; /***/ public String readLastModifiedFailed; + /***/ public String readPipeIsNotAllowed; + /***/ public String readPipeIsNotAllowedRequiredPermission; /***/ public String readTimedOut; /***/ public String receivePackObjectTooLarge1; /***/ public String receivePackObjectTooLarge2; @@ -605,6 +609,7 @@ public class JGitText extends TranslationBundle { /***/ public String refAlreadyExists1; /***/ public String reflogEntryNotFound; /***/ public String refNotResolved; + /***/ public String refTableRecordsMustIncrease; /***/ public String refUpdateReturnCodeWas; /***/ public String remoteConfigHasNoURIAssociated; /***/ public String remoteDoesNotHaveSpec; @@ -663,6 +668,7 @@ public class JGitText extends TranslationBundle { /***/ public String signingNotSupportedOnTag; /***/ public String similarityScoreMustBeWithinBounds; /***/ public String skipMustBeNonNegative; + /***/ public String skipNotAccessiblePath; /***/ public String smartHTTPPushDisabled; /***/ public String sourceDestinationMustMatch; /***/ public String sourceIsNotAWildcard; 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 258cceebee..968ade6700 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 @@ -171,7 +171,7 @@ public class ObjectDirectory extends FileObjectDatabase { infoDirectory = new File(objects, "info"); //$NON-NLS-1$ packDirectory = new File(objects, "pack"); //$NON-NLS-1$ preservedDirectory = new File(packDirectory, "preserved"); //$NON-NLS-1$ - alternatesFile = new File(infoDirectory, "alternates"); //$NON-NLS-1$ + alternatesFile = new File(objects, Constants.INFO_ALTERNATES); packList = new AtomicReference<>(NO_PACKS); unpackedObjectCache = new UnpackedObjectCache(); this.fs = fs; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java index 72699b0438..43bd9d4f58 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java @@ -313,6 +313,14 @@ public abstract class PackIndex int matchLimit) throws IOException; /** + * @return the checksum of the pack; caller must not modify it + * @since 5.5 + */ + public byte[] getChecksum() { + return packChecksum; + } + + /** * Represent mutable entry of pack index consisting of object id and offset * in pack (both mutable). * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/CachedPackUriProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/CachedPackUriProvider.java new file mode 100644 index 0000000000..5cbc2baeb2 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/CachedPackUriProvider.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019, Google LLC. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.internal.storage.pack; + +import java.io.IOException; +import java.util.Collection; +import org.eclipse.jgit.annotations.Nullable; + +/** + * Provider of URIs corresponding to cached packs. For use with the + * "packfile-uris" feature. + * @since 5.5 + */ +public interface CachedPackUriProvider { + + /** + * @param pack the cached pack for which to check if a corresponding URI + * exists + * @param protocolsSupported the protocols that the client has declared + * support for; if a URI is returned, it must be of one of these + * protocols + * @throws IOException implementations may throw this + * @return if a URI corresponds to the cached pack, an object + * containing the URI and some other information; null otherwise + * @since 5.5 + */ + @Nullable + PackInfo getInfo(CachedPack pack, Collection<String> protocolsSupported) + throws IOException; + + /** + * Information about a packfile. + * @since 5.5 + */ + public static class PackInfo { + private final String hash; + private final String uri; + + /** + * Constructs an object containing information about a packfile. + * @param hash the hash of the packfile as a hexadecimal string + * @param uri the URI corresponding to the packfile + */ + public PackInfo(String hash, String uri) { + this.hash = hash; + this.uri = uri; + } + + /** + * @return the hash of the packfile as a hexadecimal string + */ + public String getHash() { + return hash; + } + + /** + * @return the URI corresponding to the packfile + */ + public String getUri() { + return uri; + } + } +} 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 6506789218..43067d364d 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 @@ -122,6 +122,7 @@ import org.eclipse.jgit.storage.pack.PackConfig; import org.eclipse.jgit.storage.pack.PackStatistics; import org.eclipse.jgit.transport.FilterSpec; import org.eclipse.jgit.transport.ObjectCountCallback; +import org.eclipse.jgit.transport.PacketLineOut; import org.eclipse.jgit.transport.WriteAbortedException; import org.eclipse.jgit.util.BlockList; import org.eclipse.jgit.util.TemporaryBuffer; @@ -307,6 +308,8 @@ public class PackWriter implements AutoCloseable { private FilterSpec filterSpec = FilterSpec.NO_FILTER; + private PackfileUriConfig packfileUriConfig; + /** * Create writer for specified repository. * <p> @@ -651,6 +654,14 @@ public class PackWriter implements AutoCloseable { } /** + * @param config configuration related to packfile URIs + * @since 5.5 + */ + public void setPackfileUriConfig(PackfileUriConfig config) { + packfileUriConfig = config; + } + + /** * Returns objects number in a pack file that was created by this writer. * * @return number of objects in pack. @@ -673,6 +684,26 @@ public class PackWriter implements AutoCloseable { return stats.totalObjects; } + private long getUnoffloadedObjectCount() throws IOException { + long objCnt = 0; + + objCnt += objectsLists[OBJ_COMMIT].size(); + objCnt += objectsLists[OBJ_TREE].size(); + objCnt += objectsLists[OBJ_BLOB].size(); + objCnt += objectsLists[OBJ_TAG].size(); + + for (CachedPack pack : cachedPacks) { + CachedPackUriProvider.PackInfo packInfo = + packfileUriConfig.cachedPackUriProvider.getInfo( + pack, packfileUriConfig.protocolsSupported); + if (packInfo == null) { + objCnt += pack.getObjectCount(); + } + } + + return objCnt; + } + /** * Returns the object ids in the pack file that was created by this writer. * <p> @@ -1177,13 +1208,38 @@ public class PackWriter implements AutoCloseable { : new CheckedOutputStream(packStream, crc32), this); - long objCnt = getObjectCount(); + long objCnt = packfileUriConfig == null ? getObjectCount() : + getUnoffloadedObjectCount(); stats.totalObjects = objCnt; if (callback != null) callback.setObjectCount(objCnt); beginPhase(PackingPhase.WRITING, writeMonitor, objCnt); long writeStart = System.currentTimeMillis(); try { + List<CachedPack> unwrittenCachedPacks; + + if (packfileUriConfig != null) { + unwrittenCachedPacks = new ArrayList<>(); + CachedPackUriProvider p = packfileUriConfig.cachedPackUriProvider; + PacketLineOut o = packfileUriConfig.pckOut; + + o.writeString("packfile-uris\n"); //$NON-NLS-1$ + for (CachedPack pack : cachedPacks) { + CachedPackUriProvider.PackInfo packInfo = p.getInfo( + pack, packfileUriConfig.protocolsSupported); + if (packInfo != null) { + o.writeString(packInfo.getHash() + ' ' + + packInfo.getUri() + '\n'); + } else { + unwrittenCachedPacks.add(pack); + } + } + packfileUriConfig.pckOut.writeDelim(); + packfileUriConfig.pckOut.writeString("packfile\n"); //$NON-NLS-1$ + } else { + unwrittenCachedPacks = cachedPacks; + } + out.writeFileHeader(PACK_VERSION_GENERATED, objCnt); out.flush(); @@ -1197,7 +1253,7 @@ public class PackWriter implements AutoCloseable { } stats.reusedPacks = Collections.unmodifiableList(cachedPacks); - for (CachedPack pack : cachedPacks) { + for (CachedPack pack : unwrittenCachedPacks) { long deltaCnt = pack.getDeltaCount(); stats.reusedObjects += pack.getObjectCount(); stats.reusedDeltas += deltaCnt; @@ -2426,4 +2482,37 @@ public class PackWriter implements AutoCloseable { return "PackWriter.State[" + phase + ", memory=" + bytesUsed + "]"; } } + + /** + * Configuration related to the packfile URI feature. + * + * @since 5.5 + */ + public static class PackfileUriConfig { + @NonNull + private final PacketLineOut pckOut; + + @NonNull + private final Collection<String> protocolsSupported; + + @NonNull + private final CachedPackUriProvider cachedPackUriProvider; + + /** + * @param pckOut where to write "packfile-uri" lines to (should + * output to the same stream as the one passed to + * PackWriter#writePack) + * @param protocolsSupported list of protocols supported (e.g. "https") + * @param cachedPackUriProvider provider of URIs corresponding + * to cached packs + * @since 5.5 + */ + public PackfileUriConfig(@NonNull PacketLineOut pckOut, + @NonNull Collection<String> protocolsSupported, + @NonNull CachedPackUriProvider cachedPackUriProvider) { + this.pckOut = pckOut; + this.protocolsSupported = protocolsSupported; + this.cachedPackUriProvider = cachedPackUriProvider; + } + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java index 44529bfff2..b66751b94c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java @@ -84,7 +84,11 @@ import org.eclipse.jgit.util.LongList; import org.eclipse.jgit.util.NB; import org.eclipse.jgit.util.RawParseUtils; -/** Reads a single block for {@link ReftableReader}. */ +/** + * Reads a single block for {@link ReftableReader}. Instances are tied to a + * specific block in the file so are not reused for other blocks. Instances hold + * an offset into the block. + */ class BlockReader { private byte blockType; private long endPosition; @@ -141,6 +145,8 @@ class BlockReader { return RawParseUtils.decode(UTF_8, nameBuf, 0, len); } + // Matches the key against a name or a prefix. For reflogs, only the + // refname is matched, and the updateIndex suffix is ignored. boolean match(byte[] match, boolean matchIsPrefix) { int len = nameLen; if (blockType == LOG_BLOCK_TYPE) { @@ -551,7 +557,9 @@ class BlockReader { } private short readInt16() { - return (short) NB.decodeUInt16(buf, ptr += 2); + short result =(short) NB.decodeUInt16(buf, ptr); + ptr += 2; + return result; } private int readVarint32() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java index bf3a9aeca0..4f0ff2d1d1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java @@ -255,6 +255,32 @@ public class ReftableReader extends Reftable { block.seekKey(key); return block; } + if (blockType == LOG_BLOCK_TYPE) { + // No index. Log blocks are irregularly sized, so we can't do binary + // search between blocks. Scan over blocks instead. + BlockReader block = readBlock(startPos, endPos); + + for (;;) { + if (block == null || block.type() != LOG_BLOCK_TYPE) { + return null; + } + + int result = block.seekKey(key); + if (result <= 0) { + // == 0 : we found the key. + // < 0 : the key is before this block. Either the ref name is there + // but only at a newer updateIndex, or it is absent. We leave it to + // logcursor to distinguish between both cases. + return block; + } + + long pos = block.endPosition(); + if (pos >= endPos) { + return null; + } + block = readBlock(pos, endPos); + } + } return binarySearch(blockType, key, startPos, endPos); } @@ -529,12 +555,20 @@ public class ReftableReader extends Reftable { private class LogCursorImpl extends LogCursor { private final long scanEnd; private final byte[] match; - + private String refName; private long updateIndex; private ReflogEntry entry; BlockReader block; + /** + * Scans logs from this table until scanEnd position. + * + * @param scanEnd + * end of the log data in the reftable. + * @param match + * if non-null, limits the scan to precisely that refname. + */ LogCursorImpl(long scanEnd, byte[] match) { this.scanEnd = scanEnd; this.match = match; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableWriter.java index f8b9ffd176..6459c2763d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableWriter.java @@ -44,6 +44,7 @@ package org.eclipse.jgit.internal.storage.reftable; import static java.lang.Math.log; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.internal.storage.reftable.BlockWriter.padBetweenBlocks; import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_FOOTER_LEN; import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_HEADER_LEN; @@ -59,6 +60,7 @@ import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH; import java.io.IOException; import java.io.OutputStream; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -69,6 +71,7 @@ import java.util.Set; import java.util.zip.CRC32; import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.reftable.BlockWriter.DeleteLogEntry; import org.eclipse.jgit.internal.storage.reftable.BlockWriter.Entry; import org.eclipse.jgit.internal.storage.reftable.BlockWriter.IndexEntry; @@ -108,6 +111,8 @@ public class ReftableWriter { private ReftableOutputStream out; private ObjectIdSubclassMap<RefList> obj2ref; + private BlockWriter.Entry lastRef; + private BlockWriter.Entry lastLog; private BlockWriter cur; private Section refs; private Section objs; @@ -120,6 +125,8 @@ public class ReftableWriter { */ public ReftableWriter() { this(new ReftableConfig()); + lastRef = null; + lastLog = null; } /** @@ -269,10 +276,22 @@ public class ReftableWriter { throw new IllegalArgumentException(); } long d = updateIndex - minUpdateIndex; - long blockPos = refs.write(new RefEntry(ref, d)); + RefEntry entry = new RefEntry(ref, d); + if (lastRef != null && Entry.compare(lastRef, entry) >= 0) { + throwIllegalEntry(lastRef, entry); + } + lastRef = entry; + + long blockPos = refs.write(entry); indexRef(ref, blockPos); } + private void throwIllegalEntry(Entry last, Entry now) { + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().refTableRecordsMustIncrease, + new String(last.key, UTF_8), new String(now.key, UTF_8))); + } + private void indexRef(Ref ref, long blockPos) { if (indexObjects && !ref.isSymbolic()) { indexId(ref.getObjectId(), blockPos); @@ -322,7 +341,12 @@ public class ReftableWriter { throws IOException { String msg = message != null ? message : ""; //$NON-NLS-1$ beginLog(); - logs.write(new LogEntry(ref, updateIndex, who, oldId, newId, msg)); + LogEntry entry = new LogEntry(ref, updateIndex, who, oldId, newId, msg); + if (lastLog != null && Entry.compare(lastLog, entry) >= 0) { + throwIllegalEntry(lastLog, entry); + } + lastLog = entry; + logs.write(entry); } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java index d1cf1cd9ae..96e50667b3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java @@ -681,7 +681,7 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re */ protected void setupInternals() throws IOException { if (getObjectDirectory() == null && getGitDir() != null) - setObjectDirectory(safeFS().resolve(getGitDir(), "objects")); //$NON-NLS-1$ + setObjectDirectory(safeFS().resolve(getGitDir(), Constants.OBJECTS)); } /** 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 8f4468eef2..a084c82871 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -275,9 +275,27 @@ public final class Constants { /** Logs folder name */ public static final String LOGS = "logs"; + /** + * Objects folder name + * @since 5.5 + */ + public static final String OBJECTS = "objects"; + /** Info refs folder */ public static final String INFO_REFS = "info/refs"; + /** + * Info alternates file (goes under OBJECTS) + * @since 5.5 + */ + public static final String INFO_ALTERNATES = "info/alternates"; + + /** + * HTTP alternates file (goes under OBJECTS) + * @since 5.5 + */ + public static final String INFO_HTTP_ALTERNATES = "info/http-alternates"; + /** Packed refs file */ public static final String PACKED_REFS = "packed-refs"; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java index 27befba5a4..fa113bfc6b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java @@ -474,7 +474,7 @@ public class RepositoryCache { * Git directory. */ public static boolean isGitRepository(File dir, FS fs) { - return fs.resolve(dir, "objects").exists() //$NON-NLS-1$ + return fs.resolve(dir, Constants.OBJECTS).exists() && fs.resolve(dir, "refs").exists() //$NON-NLS-1$ && isValidHead(new File(dir, Constants.HEAD)); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java index 0d44317658..11db7c5998 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java @@ -50,6 +50,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; +import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; @@ -72,6 +73,8 @@ import org.bouncycastle.gpg.keybox.UserID; import org.bouncycastle.gpg.keybox.jcajce.JcaKeyBoxBuilder; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; @@ -97,6 +100,13 @@ import org.slf4j.LoggerFactory; */ class BouncyCastleGpgKeyLocator { + /** Thrown if a keybox file exists but doesn't contain an OpenPGP key. */ + private static class NoOpenPgpKeyException extends Exception { + + private static final long serialVersionUID = 1L; + + } + private static final Logger log = LoggerFactory .getLogger(BouncyCastleGpgKeyLocator.class); @@ -108,6 +118,9 @@ class BouncyCastleGpgKeyLocator { private static final Path USER_SECRET_KEY_DIR = GPG_DIRECTORY .resolve("private-keys-v1.d"); //$NON-NLS-1$ + private static final Path USER_PGP_PUBRING_FILE = GPG_DIRECTORY + .resolve("pubring.gpg"); //$NON-NLS-1$ + private static final Path USER_PGP_LEGACY_SECRING_FILE = GPG_DIRECTORY .resolve("secring.gpg"); //$NON-NLS-1$ @@ -215,13 +228,17 @@ class BouncyCastleGpgKeyLocator { * in case of problems reading the file * @throws NoSuchAlgorithmException * @throws NoSuchProviderException + * @throws NoOpenPgpKeyException + * if the file does not contain any OpenPGP key */ private PGPPublicKey findPublicKeyInKeyBox(Path keyboxFile) throws IOException, NoSuchAlgorithmException, - NoSuchProviderException { + NoSuchProviderException, NoOpenPgpKeyException { KeyBox keyBox = readKeyBoxFile(keyboxFile); + boolean hasOpenPgpKey = false; for (KeyBlob keyBlob : keyBox.getKeyBlobs()) { if (keyBlob.getType() == BlobType.OPEN_PGP_BLOB) { + hasOpenPgpKey = true; PGPPublicKey key = findPublicKeyByKeyId(keyBlob); if (key != null) { return key; @@ -232,12 +249,20 @@ class BouncyCastleGpgKeyLocator { } } } + if (!hasOpenPgpKey) { + throw new NoOpenPgpKeyException(); + } return null; } /** - * Use pubring.kbx when available, if not fallback to secring.gpg or secret - * key path provided to parse and return secret key + * If there is a private key directory containing keys, use pubring.kbx or + * pubring.gpg to find the public key; then try to find the secret key in + * the directory. + * <p> + * If there is no private key directory (or it doesn't contain any keys), + * try to find the key in secring.gpg directly. + * </p> * * @return the secret key * @throws IOException @@ -245,42 +270,112 @@ class BouncyCastleGpgKeyLocator { * @throws NoSuchAlgorithmException * @throws NoSuchProviderException * @throws PGPException - * in case of issues finding a key + * in case of issues finding a key, including no key found * @throws CanceledException * @throws URISyntaxException * @throws UnsupportedCredentialItem */ + @NonNull public BouncyCastleGpgKey findSecretKey() throws IOException, NoSuchAlgorithmException, NoSuchProviderException, PGPException, CanceledException, UnsupportedCredentialItem, URISyntaxException { - if (exists(USER_KEYBOX_PATH)) { - PGPPublicKey publicKey = // - findPublicKeyInKeyBox(USER_KEYBOX_PATH); - - if (publicKey != null) { - return findSecretKeyForKeyBoxPublicKey(publicKey, - USER_KEYBOX_PATH); - } - - throw new PGPException(MessageFormat - .format(JGitText.get().gpgNoPublicKeyFound, signingKey)); - } else if (exists(USER_PGP_LEGACY_SECRING_FILE)) { - PGPSecretKey secretKey = findSecretKeyInLegacySecring(signingKey, - USER_PGP_LEGACY_SECRING_FILE); - - if (secretKey != null) { - if (!secretKey.isSigningKey()) { + BouncyCastleGpgKey key; + PGPPublicKey publicKey = null; + if (hasKeyFiles(USER_SECRET_KEY_DIR)) { + // Use pubring.kbx or pubring.gpg to find the public key, then try + // the key files in the directory. If the public key was found in + // pubring.gpg also try secring.gpg to find the secret key. + if (exists(USER_KEYBOX_PATH)) { + try { + publicKey = findPublicKeyInKeyBox(USER_KEYBOX_PATH); + if (publicKey != null) { + key = findSecretKeyForKeyBoxPublicKey(publicKey, + USER_KEYBOX_PATH); + if (key != null) { + return key; + } + throw new PGPException(MessageFormat.format( + JGitText.get().gpgNoSecretKeyForPublicKey, + Long.toHexString(publicKey.getKeyID()))); + } throw new PGPException(MessageFormat.format( - JGitText.get().gpgNotASigningKey, signingKey)); + JGitText.get().gpgNoPublicKeyFound, signingKey)); + } catch (NoOpenPgpKeyException e) { + // There are no OpenPGP keys in the keybox at all: try the + // pubring.gpg, if it exists. + if (log.isDebugEnabled()) { + log.debug("{} does not contain any OpenPGP keys", //$NON-NLS-1$ + USER_KEYBOX_PATH); + } } - return new BouncyCastleGpgKey(secretKey, USER_PGP_LEGACY_SECRING_FILE); } - + if (exists(USER_PGP_PUBRING_FILE)) { + publicKey = findPublicKeyInPubring(USER_PGP_PUBRING_FILE); + if (publicKey != null) { + // GPG < 2.1 may have both; the agent using the directory + // and gpg using secring.gpg. GPG >= 2.1 delegates all + // secret key handling to the agent and doesn't use + // secring.gpg at all, even if it exists. Which means for us + // we have to try both since we don't know which GPG version + // the user has. + key = findSecretKeyForKeyBoxPublicKey(publicKey, + USER_PGP_PUBRING_FILE); + if (key != null) { + return key; + } + } + } + if (publicKey == null) { + throw new PGPException(MessageFormat.format( + JGitText.get().gpgNoPublicKeyFound, signingKey)); + } + // We found a public key, but didn't find the secret key in the + // private key directory. Go try the secring.gpg. + } + boolean hasSecring = false; + if (exists(USER_PGP_LEGACY_SECRING_FILE)) { + hasSecring = true; + key = loadKeyFromSecring(USER_PGP_LEGACY_SECRING_FILE); + if (key != null) { + return key; + } + } + if (publicKey != null) { + throw new PGPException(MessageFormat.format( + JGitText.get().gpgNoSecretKeyForPublicKey, + Long.toHexString(publicKey.getKeyID()))); + } else if (hasSecring) { + // publicKey == null: user has _only_ pubring.gpg/secring.gpg. throw new PGPException(MessageFormat.format( JGitText.get().gpgNoKeyInLegacySecring, signingKey)); + } else { + throw new PGPException(JGitText.get().gpgNoKeyring); } + } - throw new PGPException(JGitText.get().gpgNoKeyring); + private boolean hasKeyFiles(Path dir) { + try (DirectoryStream<Path> contents = Files.newDirectoryStream(dir, + "*.key")) { //$NON-NLS-1$ + return contents.iterator().hasNext(); + } catch (IOException e) { + // Not a directory, or something else + return false; + } + } + + private BouncyCastleGpgKey loadKeyFromSecring(Path secring) + throws IOException, PGPException { + PGPSecretKey secretKey = findSecretKeyInLegacySecring(signingKey, + secring); + + if (secretKey != null) { + if (!secretKey.isSigningKey()) { + throw new PGPException(MessageFormat + .format(JGitText.get().gpgNotASigningKey, signingKey)); + } + return new BouncyCastleGpgKey(secretKey, secring); + } + return null; } private BouncyCastleGpgKey findSecretKeyForKeyBoxPublicKey( @@ -315,9 +410,7 @@ class BouncyCastleGpgKeyLocator { } passphrasePrompt.clear(); - throw new PGPException(MessageFormat.format( - JGitText.get().gpgNoSecretKeyForPublicKey, - Long.toHexString(publicKey.getKeyID()))); + return null; } catch (RuntimeException e) { passphrasePrompt.clear(); throw e; @@ -379,13 +472,62 @@ class BouncyCastleGpgKeyLocator { return null; } + /** + * Return the first public key matching the key id ({@link #signingKey}. + * + * @param pubringFile + * + * @return the PGP public key, or {@code null} if none found + * @throws IOException + * on I/O related errors + * @throws PGPException + * on BouncyCastle errors + */ + private PGPPublicKey findPublicKeyInPubring(Path pubringFile) + throws IOException, PGPException { + try (InputStream in = newInputStream(pubringFile)) { + PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection( + new BufferedInputStream(in), + new JcaKeyFingerprintCalculator()); + + String keyId = signingKey.toLowerCase(Locale.ROOT); + Iterator<PGPPublicKeyRing> keyrings = pgpPub.getKeyRings(); + while (keyrings.hasNext()) { + PGPPublicKeyRing keyRing = keyrings.next(); + Iterator<PGPPublicKey> keys = keyRing.getPublicKeys(); + while (keys.hasNext()) { + PGPPublicKey key = keys.next(); + // try key id + String fingerprint = Hex.toHexString(key.getFingerprint()) + .toLowerCase(Locale.ROOT); + if (fingerprint.endsWith(keyId)) { + return key; + } + // try user id + Iterator<String> userIDs = key.getUserIDs(); + while (userIDs.hasNext()) { + String userId = userIDs.next(); + if (containsSigningKey(userId)) { + return key; + } + } + } + } + } + return null; + } + private PGPPublicKey getFirstPublicKey(KeyBlob keyBlob) throws IOException { return ((PublicKeyRingBlob) keyBlob).getPGPPublicKeyRing() .getPublicKey(); } private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException, - NoSuchAlgorithmException, NoSuchProviderException { + NoSuchAlgorithmException, NoSuchProviderException, + NoOpenPgpKeyException { + if (keyboxFile.toFile().length() == 0) { + throw new NoOpenPgpKeyException(); + } KeyBox keyBox; try (InputStream in = new BufferedInputStream( newInputStream(keyboxFile))) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java index 247a3bdb28..7b5e6fa310 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java @@ -49,6 +49,10 @@ abstract class AbstractRevQueue extends Generator { /** Current output flags set for this generator instance. */ int outputType; + AbstractRevQueue(boolean firstParent) { + super(firstParent); + } + /** * Add a commit to the queue. * <p> @@ -96,10 +100,15 @@ abstract class AbstractRevQueue extends Generator { */ public final void addParents(RevCommit c, RevFlag queueControl) { final RevCommit[] pList = c.parents; - if (pList == null) + if (pList == null) { return; - for (RevCommit p : pList) - add(p, queueControl); + } + for (int i = 0; i < pList.length; i++) { + if (firstParent && i > 0) { + break; + } + add(pList[i], queueControl); + } } /** @@ -138,6 +147,10 @@ abstract class AbstractRevQueue extends Generator { } private static class AlwaysEmptyQueue extends AbstractRevQueue { + private AlwaysEmptyQueue() { + super(false); + } + @Override public void add(RevCommit c) { throw new UnsupportedOperationException(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BlockRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BlockRevQueue.java index 79307b5b7c..64e9e03229 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BlockRevQueue.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BlockRevQueue.java @@ -53,13 +53,19 @@ abstract class BlockRevQueue extends AbstractRevQueue { /** * Create an empty revision queue. + * + * @param firstParent + * whether only first-parent links should be followed when + * walking */ - protected BlockRevQueue() { + protected BlockRevQueue(boolean firstParent) { + super(firstParent); free = new BlockFreeList(); } BlockRevQueue(Generator s) throws MissingObjectException, IncorrectObjectTypeException, IOException { + super(s.firstParent); free = new BlockFreeList(); outputType = s.outputType(); s.shareFreeList(this); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java index 0fd6621e62..98c99e845d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java @@ -55,6 +55,7 @@ class BoundaryGenerator extends Generator { Generator g; BoundaryGenerator(RevWalk w, Generator s) { + super(s.firstParent); g = new InitialGenerator(w, s); } @@ -86,8 +87,9 @@ class BoundaryGenerator extends Generator { private final Generator source; InitialGenerator(RevWalk w, Generator s) { + super(s.firstParent); walk = w; - held = new FIFORevQueue(); + held = new FIFORevQueue(firstParent); source = s; source.shareFreeList(held); } @@ -107,13 +109,19 @@ class BoundaryGenerator extends Generator { IncorrectObjectTypeException, IOException { RevCommit c = source.next(); if (c != null) { - for (RevCommit p : c.parents) - if ((p.flags & UNINTERESTING) != 0) + for (int i = 0; i < c.parents.length; i++) { + if (firstParent && i > 0) { + break; + } + RevCommit p = c.parents[i]; + if ((p.flags & UNINTERESTING) != 0) { held.add(p); + } + } return c; } - final FIFORevQueue boundary = new FIFORevQueue(); + final FIFORevQueue boundary = new FIFORevQueue(firstParent); boundary.shareFreeList(held); for (;;) { c = held.next(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java index b86e876201..d77b055534 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java @@ -69,15 +69,18 @@ public class DateRevQueue extends AbstractRevQueue { private int last = -1; - /** - * Create an empty date queue. - */ + /** Create an empty date queue. */ public DateRevQueue() { - super(); + super(false); + } + + DateRevQueue(boolean firstParent) { + super(firstParent); } DateRevQueue(Generator s) throws MissingObjectException, IncorrectObjectTypeException, IOException { + super(s.firstParent); for (;;) { final RevCommit c = s.next(); if (c == null) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DelayRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DelayRevQueue.java index c397a01317..9134e08b28 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DelayRevQueue.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DelayRevQueue.java @@ -70,6 +70,7 @@ final class DelayRevQueue extends Generator { private int size; DelayRevQueue(Generator g) { + super(g.firstParent); pending = g; delay = new FIFORevQueue(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java index 5199a2927d..0b04d5d174 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java @@ -95,7 +95,8 @@ class DepthGenerator extends Generator { */ DepthGenerator(DepthWalk w, Generator s) throws MissingObjectException, IncorrectObjectTypeException, IOException { - pending = new FIFORevQueue(); + super(s.firstParent); + pending = new FIFORevQueue(firstParent); walk = (RevWalk)w; this.depth = w.getDepth(); @@ -196,7 +197,11 @@ class DepthGenerator extends Generator { int newDepth = c.depth + 1; - for (RevCommit p : c.parents) { + for (int i = 0; i < c.parents.length; i++) { + if (firstParent && i > 0) { + break; + } + RevCommit p = c.parents[i]; DepthWalk.Commit dp = (DepthWalk.Commit) p; // If no depth has been assigned to this commit, assign diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/EndGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/EndGenerator.java index 627e1c7a51..03916c8152 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/EndGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/EndGenerator.java @@ -47,7 +47,7 @@ class EndGenerator extends Generator { static final EndGenerator INSTANCE = new EndGenerator(); private EndGenerator() { - // We have nothing to initialize. + super(false); } @Override diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FIFORevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FIFORevQueue.java index cdb084c15e..40ee55c730 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FIFORevQueue.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FIFORevQueue.java @@ -56,11 +56,13 @@ public class FIFORevQueue extends BlockRevQueue { private Block tail; - /** - * Create an empty FIFO queue. - */ + /** Create an empty FIFO queue. */ public FIFORevQueue() { - super(); + super(false); + } + + FIFORevQueue(boolean firstParent) { + super(firstParent); } FIFORevQueue(Generator s) throws MissingObjectException, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FixUninterestingGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FixUninterestingGenerator.java index 4e6d7e681d..289842a909 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FixUninterestingGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FixUninterestingGenerator.java @@ -62,6 +62,7 @@ final class FixUninterestingGenerator extends Generator { private final Generator pending; FixUninterestingGenerator(Generator g) { + super(g.firstParent); pending = g; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/Generator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/Generator.java index b2c92ea249..59c5cce828 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/Generator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/Generator.java @@ -75,6 +75,12 @@ abstract class Generator { /** Output may have {@link RevWalk#UNINTERESTING} marked on it. */ static final int HAS_UNINTERESTING = 1 << 4; + protected final boolean firstParent; + + protected Generator(boolean firstParent) { + this.firstParent = firstParent; + } + /** * Connect the supplied queue to this generator's own free list (if any). * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/LIFORevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/LIFORevQueue.java index 846b8d92fa..5628d359bf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/LIFORevQueue.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/LIFORevQueue.java @@ -59,7 +59,7 @@ public class LIFORevQueue extends BlockRevQueue { * Create an empty LIFO queue. */ public LIFORevQueue() { - super(); + super(false); } LIFORevQueue(Generator s) throws MissingObjectException, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java index 2fe95318b2..4ea57cb51b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java @@ -85,8 +85,9 @@ class MergeBaseGenerator extends Generator { private CarryStack stack; MergeBaseGenerator(RevWalk w) { + super(w.isFirstParent()); walker = w; - pending = new DateRevQueue(); + pending = new DateRevQueue(firstParent); } void init(AbstractRevQueue p) throws IOException { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java index e607b7daa0..3990dd6d0e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java @@ -109,6 +109,7 @@ class PendingGenerator extends Generator { PendingGenerator(final RevWalk w, final DateRevQueue p, final RevFilter f, final int out) { + super(w.isFirstParent()); walker = w; pending = p; filter = f; @@ -140,7 +141,13 @@ class PendingGenerator extends Generator { produce = filter.include(walker, c); } - for (RevCommit p : c.parents) { + for (int i = 0; i < c.parents.length; i++) { + RevCommit p = c.parents[i]; + // If the commit is uninteresting, don't try to prune + // parents because we want the maximal uninteresting set. + if (firstParent && i > 0 && (c.flags & UNINTERESTING) == 0) { + continue; + } if ((p.flags & SEEN) != 0) continue; if ((p.flags & PARSED) == 0) 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 f50d189ce5..4aa83252b5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java @@ -201,6 +201,8 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { private boolean rewriteParents = true; + private boolean firstParent; + boolean shallowCommitsInitialized; /** @@ -233,7 +235,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { idBuffer = new MutableObjectId(); objects = new ObjectIdOwnerMap<>(); roots = new ArrayList<>(); - queue = new DateRevQueue(); + queue = new DateRevQueue(false); pending = new StartGenerator(this); sorting = EnumSet.of(RevSort.NONE); filter = RevFilter.ALL; @@ -664,6 +666,35 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { } /** + * @return whether only first-parent links should be followed when walking. + * + * @since 5.5 + */ + public boolean isFirstParent() { + return firstParent; + } + + /** + * Set whether or not only first parent links should be followed. + * <p> + * If set, second- and higher-parent links are not traversed at all. + * <p> + * This must be called prior to {@link #markStart(RevCommit)}. + * + * @param enable + * true to walk only first-parent links. + * + * @since 5.5 + */ + public void setFirstParent(boolean enable) { + assertNotStarted(); + assertNoCommitsMarkedStart(); + firstParent = enable; + queue = new DateRevQueue(firstParent); + pending = new StartGenerator(this); + } + + /** * Locate a reference to a blob without loading it. * <p> * The blob may or may not exist in the repository. It is impossible to tell @@ -1295,7 +1326,8 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { * <p> * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit) * instances are not invalidated. RevFlag instances are not invalidated, but - * are removed from all RevObjects. + * are removed from all RevObjects. The value of {@code firstParent} is + * retained. * * @param retainFlags * application flags that should <b>not</b> be cleared from @@ -1331,7 +1363,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { } roots.clear(); - queue = new DateRevQueue(); + queue = new DateRevQueue(firstParent); pending = new StartGenerator(this); } @@ -1349,9 +1381,10 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { delayFreeFlags = 0; retainOnReset = 0; carryFlags = UNINTERESTING; + firstParent = false; objects.clear(); roots.clear(); - queue = new DateRevQueue(); + queue = new DateRevQueue(firstParent); pending = new StartGenerator(this); shallowCommitsInitialized = false; } @@ -1423,6 +1456,21 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { throw new IllegalStateException(JGitText.get().outputHasAlreadyBeenStarted); } + /** + * Throws an exception if any commits have been marked as start. + * <p> + * If {@link #markStart(RevCommit)} has already been called, + * {@link #reset()} can be called to satisfy this condition. + * + * @since 5.5 + */ + protected void assertNoCommitsMarkedStart() { + if (roots.isEmpty()) + return; + throw new IllegalStateException( + JGitText.get().commitsHaveAlreadyBeenMarkedAsStart); + } + private boolean isNotStarted() { return pending instanceof StartGenerator; } 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 1c868ffe08..2e26641ebc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java @@ -77,6 +77,7 @@ class RewriteGenerator extends Generator { private final Generator source; RewriteGenerator(Generator s) { + super(s.firstParent); source = s; } @@ -102,6 +103,10 @@ class RewriteGenerator extends Generator { final int nParents = pList.length; for (int i = 0; i < nParents; i++) { final RevCommit oldp = pList[i]; + if (firstParent && i > 0) { + c.parents = new RevCommit[] { rewrite(oldp) }; + return c; + } final RevCommit newp = rewrite(oldp); if (oldp != newp) { pList[i] = newp; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java index eb129a2631..b309d6fee2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java @@ -67,6 +67,7 @@ class StartGenerator extends Generator { private final RevWalk walker; StartGenerator(RevWalk w) { + super(w.isFirstParent()); walker = w; } @@ -89,9 +90,14 @@ class StartGenerator extends Generator { // Computing for merge bases is a special case and does not // use the bulk of the generator pipeline. // - if (tf != TreeFilter.ALL) + if (tf != TreeFilter.ALL) { throw new IllegalStateException(MessageFormat.format( JGitText.get().cannotCombineTreeFilterWithRevFilter, tf, rf)); + } + if (w.isFirstParent()) { + throw new IllegalStateException( + JGitText.get().cannotFindMergeBaseUsingFirstParent); + } final MergeBaseGenerator mbg = new MergeBaseGenerator(w); walker.pending = mbg; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java index 645034351f..a2c9ef6da4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java @@ -71,15 +71,21 @@ class TopoSortGenerator extends Generator { */ TopoSortGenerator(Generator s) throws MissingObjectException, IncorrectObjectTypeException, IOException { - pending = new FIFORevQueue(); + super(s.firstParent); + pending = new FIFORevQueue(firstParent); outputType = s.outputType() | SORT_TOPO; s.shareFreeList(pending); for (;;) { final RevCommit c = s.next(); - if (c == null) + if (c == null) { break; - for (RevCommit p : c.parents) - p.inDegree++; + } + for (int i = 0; i < c.parents.length; i++) { + if (firstParent && i > 0) { + break; + } + c.parents[i].inDegree++; + } pending.add(c); } } @@ -113,7 +119,11 @@ class TopoSortGenerator extends Generator { // All of our children have already produced, // so it is OK for us to produce now as well. // - for (RevCommit p : c.parents) { + for (int i = 0; i < c.parents.length; i++) { + if (firstParent && i > 0) { + break; + } + RevCommit p = c.parents[i]; if (--p.inDegree == 0 && (p.flags & TOPO_DELAY) != 0) { // This parent tried to come before us, but we are // his last child. unpop the parent so it goes right diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java index 11301538e6..f7c3218850 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java @@ -116,8 +116,7 @@ public class TreeRevFilter extends RevFilter { * @param rewriteFlag * flag to color commits to be removed from the simplified DAT. */ - TreeRevFilter(final RevWalk walker, final TreeFilter t, - final int rewriteFlag) { + TreeRevFilter(RevWalk walker, TreeFilter t, int rewriteFlag) { pathFilter = new TreeWalk(walker.reader); pathFilter.setFilter(t); pathFilter.setRecursive(t.shouldBeRecursive()); @@ -137,14 +136,15 @@ public class TreeRevFilter extends RevFilter { IncorrectObjectTypeException, IOException { // Reset the tree filter to scan this commit and parents. // - final RevCommit[] pList = c.parents; - final int nParents = pList.length; - final TreeWalk tw = pathFilter; - final ObjectId[] trees = new ObjectId[nParents + 1]; + RevCommit[] pList = c.parents; + int nParents = pList.length; + TreeWalk tw = pathFilter; + ObjectId[] trees = new ObjectId[nParents + 1]; for (int i = 0; i < nParents; i++) { - final RevCommit p = c.parents[i]; - if ((p.flags & PARSED) == 0) + RevCommit p = c.parents[i]; + if ((p.flags & PARSED) == 0) { p.parseHeaders(walker); + } trees[i] = p.getTree(); } trees[nParents] = c.getTree(); @@ -156,10 +156,11 @@ public class TreeRevFilter extends RevFilter { int chgs = 0, adds = 0; while (tw.next()) { chgs++; - if (tw.getRawMode(0) == 0 && tw.getRawMode(1) != 0) + if (tw.getRawMode(0) == 0 && tw.getRawMode(1) != 0) { adds++; - else + } else { break; // no point in looking at this further. + } } if (chgs == 0) { @@ -185,8 +186,9 @@ public class TreeRevFilter extends RevFilter { // We have no parents to compare against. Consider us to be // REWRITE only if we have no paths matching our filter. // - if (tw.next()) + if (tw.next()) { return true; + } c.flags |= rewriteFlag; return false; } @@ -196,18 +198,19 @@ public class TreeRevFilter extends RevFilter { // it does not contribute changes to us. Such a parent may be an // uninteresting side branch. // - final int[] chgs = new int[nParents]; - final int[] adds = new int[nParents]; + int[] chgs = new int[nParents]; + int[] adds = new int[nParents]; while (tw.next()) { - final int myMode = tw.getRawMode(nParents); + int myMode = tw.getRawMode(nParents); for (int i = 0; i < nParents; i++) { - final int pMode = tw.getRawMode(i); - if (myMode == pMode && tw.idEqual(i, nParents)) + int pMode = tw.getRawMode(i); + if (myMode == pMode && tw.idEqual(i, nParents)) { continue; - + } chgs[i]++; - if (pMode == 0 && myMode != 0) + if (pMode == 0 && myMode != 0) { adds[i]++; + } } } @@ -220,7 +223,7 @@ public class TreeRevFilter extends RevFilter { // parent commit. // - final RevCommit p = pList[i]; + RevCommit p = pList[i]; if ((p.flags & UNINTERESTING) != 0) { // This parent was marked as not interesting by the // application. We should look for another parent 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 57d6bc2466..27cc205262 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java @@ -129,13 +129,13 @@ public abstract class BasePackFetchConnection extends BasePackConnection public static final String OPTION_INCLUDE_TAG = GitProtocolConstants.OPTION_INCLUDE_TAG; /** - * Mutli-ACK support for improved negotiation. + * Multi-ACK support for improved negotiation. * @since 2.0 */ public static final String OPTION_MULTI_ACK = GitProtocolConstants.OPTION_MULTI_ACK; /** - * Mutli-ACK detailed support for improved negotiation. + * Multi-ACK detailed support for improved negotiation. * @since 2.0 */ public static final String OPTION_MULTI_ACK_DETAILED = GitProtocolConstants.OPTION_MULTI_ACK_DETAILED; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java index 6c24269095..fe1b697612 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java @@ -72,6 +72,11 @@ public final class FetchV2Request extends FetchRequest { @NonNull private final List<String> serverOptions; + private final boolean sidebandAll; + + @NonNull + private final List<String> packfileUriProtocols; + FetchV2Request(@NonNull List<ObjectId> peerHas, @NonNull List<String> wantedRefs, @NonNull Set<ObjectId> wantIds, @@ -79,7 +84,8 @@ public final class FetchV2Request extends FetchRequest { @NonNull List<String> deepenNotRefs, int depth, @NonNull FilterSpec filterSpec, boolean doneReceived, @NonNull Set<String> clientCapabilities, - @Nullable String agent, @NonNull List<String> serverOptions) { + @Nullable String agent, @NonNull List<String> serverOptions, + boolean sidebandAll, @NonNull List<String> packfileUriProtocols) { super(wantIds, depth, clientShallowCommits, filterSpec, clientCapabilities, deepenSince, deepenNotRefs, agent); @@ -87,6 +93,8 @@ public final class FetchV2Request extends FetchRequest { this.wantedRefs = requireNonNull(wantedRefs); this.doneReceived = doneReceived; this.serverOptions = requireNonNull(serverOptions); + this.sidebandAll = sidebandAll; + this.packfileUriProtocols = packfileUriProtocols; } /** @@ -127,6 +135,18 @@ public final class FetchV2Request extends FetchRequest { return serverOptions; } + /** + * @return true if "sideband-all" was received + */ + boolean getSidebandAll() { + return sidebandAll; + } + + @NonNull + List<String> getPackfileUriProtocols() { + return packfileUriProtocols; + } + /** @return A builder of {@link FetchV2Request}. */ static Builder builder() { return new Builder(); @@ -159,6 +179,10 @@ public final class FetchV2Request extends FetchRequest { final List<String> serverOptions = new ArrayList<>(); + boolean sidebandAll; + + final List<String> packfileUriProtocols = new ArrayList<>(); + private Builder() { } @@ -318,13 +342,29 @@ public final class FetchV2Request extends FetchRequest { } /** + * @param value true if client sent "sideband-all" + * @return this builder + */ + Builder setSidebandAll(boolean value) { + sidebandAll = value; + return this; + } + + Builder addPackfileUriProtocol(@NonNull String value) { + packfileUriProtocols.add(value); + return this; + } + + /** * @return Initialized fetch request */ FetchV2Request build() { return new FetchV2Request(peerHas, wantedRefs, wantIds, clientShallowCommits, deepenSince, deepenNotRefs, depth, filterSpec, doneReceived, clientCapabilities, - agent, Collections.unmodifiableList(serverOptions)); + agent, Collections.unmodifiableList(serverOptions), + sidebandAll, + Collections.unmodifiableList(packfileUriProtocols)); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java index 1561c93b95..e3c0bc629a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java @@ -59,14 +59,14 @@ public final class GitProtocolConstants { public static final String OPTION_INCLUDE_TAG = "include-tag"; //$NON-NLS-1$ /** - * Mutli-ACK support for improved negotiation. + * Multi-ACK support for improved negotiation. * * @since 3.2 */ public static final String OPTION_MULTI_ACK = "multi_ack"; //$NON-NLS-1$ /** - * Mutli-ACK detailed support for improved negotiation. + * Multi-ACK detailed support for improved negotiation. * * @since 3.2 */ @@ -174,6 +174,14 @@ public final class GitProtocolConstants { public static final String OPTION_WANT_REF = "want-ref"; //$NON-NLS-1$ /** + * The client requested that the whole response be multiplexed, with + * each non-flush and non-delim pkt prefixed by a sideband designator. + * + * @since 5.5 + */ + public static final String OPTION_SIDEBAND_ALL = "sideband-all"; //$NON-NLS-1$ + + /** * The client supports atomic pushes. If this option is used, the server * will update all refs within one atomic transaction. * 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 0bdd6ba812..79af1ebc60 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java @@ -551,7 +551,7 @@ public abstract class JschConfigSessionFactory extends SshSessionFactory { * @param config * to use */ - void setConfig(OpenSshConfig config) { + synchronized void setConfig(OpenSshConfig config) { this.config = config; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java index e9400919a8..7f837bb841 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java @@ -74,6 +74,8 @@ public class PacketLineOut { private boolean flushOnEnd; + private boolean usingSideband; + /** * Create a new packet line writer. * @@ -98,6 +100,28 @@ public class PacketLineOut { } /** + * @return whether to add a sideband designator to each non-flush and + * non-delim packet + * @see #setUsingSideband + * @since 5.5 + */ + public boolean isUsingSideband() { + return usingSideband; + } + + /** + * @param value If true, when writing packet lines, add, as the first + * byte, a sideband designator to each non-flush and non-delim + * packet. See pack-protocol.txt and protocol-v2.txt from the Git + * project for more information, specifically the "side-band" and + * "sideband-all" sections. + * @since 5.5 + */ + public void setUsingSideband(boolean value) { + this.usingSideband = value; + } + + /** * Write a UTF-8 encoded string as a single length-delimited packet. * * @param s @@ -139,8 +163,14 @@ public class PacketLineOut { * @since 4.5 */ public void writePacket(byte[] buf, int pos, int len) throws IOException { - formatLength(len + 4); - out.write(lenbuffer, 0, 4); + if (usingSideband) { + formatLength(len + 5); + out.write(lenbuffer, 0, 4); + out.write(1); + } else { + formatLength(len + 4); + out.write(lenbuffer, 0, 4); + } out.write(buf, pos, len); if (log.isDebugEnabled()) { String s = RawParseUtils.decode(UTF_8, buf, pos, len); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java index 542abe7563..4334888a9a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java @@ -42,7 +42,9 @@ package org.eclipse.jgit.transport; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.jgit.storage.pack.PackStatistics; @@ -55,8 +57,7 @@ import org.eclipse.jgit.storage.pack.PackStatistics; * @since 4.1 */ public class PostUploadHookChain implements PostUploadHook { - private final PostUploadHook[] hooks; - private final int count; + private final List<PostUploadHook> hooks; /** * Create a new hook chaining the given hooks together. @@ -65,29 +66,29 @@ public class PostUploadHookChain implements PostUploadHook { * hooks to execute, in order. * @return a new chain of the given hooks. */ - public static PostUploadHook newChain(List<? extends PostUploadHook> hooks) { - PostUploadHook[] newHooks = new PostUploadHook[hooks.size()]; - int i = 0; - for (PostUploadHook hook : hooks) - if (hook != PostUploadHook.NULL) - newHooks[i++] = hook; - if (i == 0) + public static PostUploadHook newChain(List<PostUploadHook> hooks) { + List<PostUploadHook> newHooks = hooks.stream() + .filter(hook -> !hook.equals(PostUploadHook.NULL)) + .collect(Collectors.toList()); + + if (newHooks.isEmpty()) { return PostUploadHook.NULL; - else if (i == 1) - return newHooks[0]; - else - return new PostUploadHookChain(newHooks, i); + } else if (newHooks.size() == 1) { + return newHooks.get(0); + } else { + return new PostUploadHookChain(newHooks); + } } /** {@inheritDoc} */ @Override public void onPostUpload(PackStatistics stats) { - for (int i = 0; i < count; i++) - hooks[i].onPostUpload(stats); + for (PostUploadHook hook : hooks) { + hook.onPostUpload(stats); + } } - private PostUploadHookChain(PostUploadHook[] hooks, int count) { - this.hooks = hooks; - this.count = count; + private PostUploadHookChain(List<PostUploadHook> hooks) { + this.hooks = Collections.unmodifiableList(hooks); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHookChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHookChain.java index bfd52af74a..2192654b85 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHookChain.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHookChain.java @@ -44,7 +44,9 @@ package org.eclipse.jgit.transport; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.jgit.lib.ObjectId; @@ -56,8 +58,7 @@ import org.eclipse.jgit.lib.ObjectId; * one hook throws an exception, execution of remaining hook methods is aborted. */ public class PreUploadHookChain implements PreUploadHook { - private final PreUploadHook[] hooks; - private final int count; + private final List<PreUploadHook> hooks; /** * Create a new hook chaining the given hooks together. @@ -66,18 +67,18 @@ public class PreUploadHookChain implements PreUploadHook { * hooks to execute, in order. * @return a new hook chain of the given hooks. */ - public static PreUploadHook newChain(List<? extends PreUploadHook> hooks) { - PreUploadHook[] newHooks = new PreUploadHook[hooks.size()]; - int i = 0; - for (PreUploadHook hook : hooks) - if (hook != PreUploadHook.NULL) - newHooks[i++] = hook; - if (i == 0) + public static PreUploadHook newChain(List<PreUploadHook> hooks) { + List<PreUploadHook> newHooks = hooks.stream() + .filter(hook -> !hook.equals(PreUploadHook.NULL)) + .collect(Collectors.toList()); + + if (newHooks.isEmpty()) { return PreUploadHook.NULL; - else if (i == 1) - return newHooks[0]; - else - return new PreUploadHookChain(newHooks, i); + } else if (newHooks.size() == 1) { + return newHooks.get(0); + } else { + return new PreUploadHookChain(newHooks); + } } /** {@inheritDoc} */ @@ -85,8 +86,9 @@ public class PreUploadHookChain implements PreUploadHook { public void onBeginNegotiateRound(UploadPack up, Collection<? extends ObjectId> wants, int cntOffered) throws ServiceMayNotContinueException { - for (int i = 0; i < count; i++) - hooks[i].onBeginNegotiateRound(up, wants, cntOffered); + for (PreUploadHook hook : hooks) { + hook.onBeginNegotiateRound(up, wants, cntOffered); + } } /** {@inheritDoc} */ @@ -95,8 +97,9 @@ public class PreUploadHookChain implements PreUploadHook { Collection<? extends ObjectId> wants, int cntCommon, int cntNotFound, boolean ready) throws ServiceMayNotContinueException { - for (int i = 0; i < count; i++) - hooks[i].onEndNegotiateRound(up, wants, cntCommon, cntNotFound, ready); + for (PreUploadHook hook : hooks) { + hook.onEndNegotiateRound(up, wants, cntCommon, cntNotFound, ready); + } } /** {@inheritDoc} */ @@ -105,12 +108,12 @@ public class PreUploadHookChain implements PreUploadHook { Collection<? extends ObjectId> wants, Collection<? extends ObjectId> haves) throws ServiceMayNotContinueException { - for (int i = 0; i < count; i++) - hooks[i].onSendPack(up, wants, haves); + for (PreUploadHook hook : hooks) { + hook.onSendPack(up, wants, haves); + } } - private PreUploadHookChain(PreUploadHook[] hooks, int count) { - this.hooks = hooks; - this.count = count; + private PreUploadHookChain(List<PreUploadHook> hooks) { + this.hooks = Collections.unmodifiableList(hooks); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2HookChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2HookChain.java new file mode 100644 index 0000000000..cc36473757 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2HookChain.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019, Google LLC. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.transport; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * {@link org.eclipse.jgit.transport.ProtocolV2Hook} that delegates to a list of + * other hooks. + * <p> + * Hooks are run in the order passed to the constructor. If running a method on + * one hook throws an exception, execution of remaining hook methods is aborted. + * + * @since 5.5 + */ +public class ProtocolV2HookChain implements ProtocolV2Hook { + private final List<? extends ProtocolV2Hook> hooks; + + /** + * Create a new hook chaining the given hooks together. + * + * @param hooks + * hooks to execute, in order. + * @return a new hook chain of the given hooks. + */ + public static ProtocolV2Hook newChain( + List<? extends ProtocolV2Hook> hooks) { + List<? extends ProtocolV2Hook> newHooks = hooks.stream() + .filter(hook -> !hook.equals(ProtocolV2Hook.DEFAULT)) + .collect(Collectors.toList()); + + if (newHooks.isEmpty()) { + return ProtocolV2Hook.DEFAULT; + } else if (newHooks.size() == 1) { + return newHooks.get(0); + } else { + return new ProtocolV2HookChain(newHooks); + } + } + + @Override + public void onCapabilities(CapabilitiesV2Request req) + throws ServiceMayNotContinueException { + for (ProtocolV2Hook hook : hooks) { + hook.onCapabilities(req); + } + } + + @Override + public void onLsRefs(LsRefsV2Request req) + throws ServiceMayNotContinueException { + for (ProtocolV2Hook hook : hooks) { + hook.onLsRefs(req); + } + } + + @Override + public void onFetch(FetchV2Request req) + throws ServiceMayNotContinueException { + for (ProtocolV2Hook hook : hooks) { + hook.onFetch(req); + } + } + + private ProtocolV2HookChain(List<? extends ProtocolV2Hook> hooks) { + this.hooks = Collections.unmodifiableList(hooks); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java index caba15fc54..14ccddfb61 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java @@ -49,6 +49,7 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SERVER_OPTION; +import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_ALL; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WANT_REF; @@ -210,6 +211,13 @@ final class ProtocolV2Parser { filterReceived = true; reqBuilder.setFilterSpec(FilterSpec.fromFilterLine( line2.substring(OPTION_FILTER.length() + 1))); + } else if (transferConfig.isAllowSidebandAll() + && line2.equals(OPTION_SIDEBAND_ALL)) { + reqBuilder.setSidebandAll(true); + } else if (line2.startsWith("packfile-uris ")) { //$NON-NLS-1$ + for (String s : line2.substring(14).split(",")) { + reqBuilder.addPackfileUriProtocol(s); + } } else { throw new PackProtocolException(MessageFormat .format(JGitText.get().unexpectedPacketLine, line2)); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java index 2b79d7105c..efbe77704b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java @@ -101,6 +101,13 @@ public final class SshConstants { /** Key in an ssh config file. */ public static final String GLOBAL_KNOWN_HOSTS_FILE = "GlobalKnownHostsFile"; + /** + * Key in an ssh config file. + * + * @since 5.5 + */ + public static final String HASH_KNOWN_HOSTS = "HashKnownHosts"; + /** Key in an ssh config file. */ public static final String HOST = "Host"; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java index a3e655cd92..758d74c3fa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java @@ -131,6 +131,7 @@ public class TransferConfig { private final boolean allowTipSha1InWant; private final boolean allowReachableSha1InWant; private final boolean allowFilter; + private final boolean allowSidebandAll; final @Nullable ProtocolVersion protocolVersion; final String[] hideRefs; @@ -210,6 +211,8 @@ public class TransferConfig { "uploadpack", "allowfilter", false); protocolVersion = ProtocolVersion.parse(rc.getString("protocol", null, "version")); hideRefs = rc.getStringList("uploadpack", null, "hiderefs"); + allowSidebandAll = rc.getBoolean( + "uploadpack", "allowsidebandall", false); } /** @@ -292,6 +295,14 @@ public class TransferConfig { } /** + * @return true if clients are allowed to specify a "sideband-all" line + * @since 5.5 + */ + public boolean isAllowSidebandAll() { + return allowSidebandAll; + } + + /** * Get {@link org.eclipse.jgit.transport.RefFilter} respecting configured * hidden refs. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java index 6118cb8770..c0a70bcd2c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java @@ -259,7 +259,7 @@ public class TransportAmazonS3 extends HttpTransport implements WalkTransport { @Override Collection<WalkRemoteObjectDatabase> getAlternates() throws IOException { try { - return readAlternates(INFO_ALTERNATES); + return readAlternates(Constants.INFO_ALTERNATES); } catch (FileNotFoundException err) { // Fall through. } 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 a591dbae2a..3029327c52 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java @@ -48,6 +48,8 @@ package org.eclipse.jgit.transport; import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.lib.Constants.HEAD; +import static org.eclipse.jgit.lib.Constants.INFO_ALTERNATES; +import static org.eclipse.jgit.lib.Constants.INFO_HTTP_ALTERNATES; import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP; import static org.eclipse.jgit.util.HttpSupport.ENCODING_X_GZIP; import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT; @@ -75,6 +77,7 @@ import java.net.HttpCookie; import java.net.MalformedURLException; import java.net.Proxy; import java.net.ProxySelector; +import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.InvalidPathException; @@ -589,7 +592,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport, JGitText.get().redirectsOff, Integer.valueOf(status))); } - URIish newUri = redirect(conn.getHeaderField(HDR_LOCATION), + URIish newUri = redirect(u, + conn.getHeaderField(HDR_LOCATION), Constants.INFO_REFS, redirects++); setURI(newUri); u = getServiceURL(service); @@ -798,7 +802,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport, } } - private URIish redirect(String location, String checkFor, int redirects) + private URIish redirect(URL currentUrl, String location, String checkFor, + int redirects) throws TransportException { if (location == null || location.isEmpty()) { throw new TransportException(uri, @@ -812,13 +817,16 @@ public class TransportHttp extends HttpTransport implements WalkTransport, location)); } try { - if (!isValidRedirect(baseUrl, location, checkFor)) { + URI redirectTo = new URI(location); + redirectTo = currentUrl.toURI().resolve(redirectTo); + String redirected = redirectTo.toASCIIString(); + if (!isValidRedirect(baseUrl, redirected, checkFor)) { throw new TransportException(uri, MessageFormat.format(JGitText.get().redirectBlocked, - baseUrl, location)); + baseUrl, redirected)); } - location = location.substring(0, location.indexOf(checkFor)); - URIish result = new URIish(location); + redirected = redirected.substring(0, redirected.indexOf(checkFor)); + URIish result = new URIish(redirected); if (LOG.isInfoEnabled()) { LOG.info(MessageFormat.format(JGitText.get().redirectHttp, uri.setPass(null), @@ -1448,7 +1456,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport, // Let openResponse() issue an error return; } - currentUri = redirect(conn.getHeaderField(HDR_LOCATION), + currentUri = redirect(conn.getURL(), + conn.getHeaderField(HDR_LOCATION), '/' + serviceName, redirects++); try { baseUrl = toURL(currentUri); 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 5c68308f90..8d91c6098a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java @@ -43,7 +43,9 @@ package org.eclipse.jgit.transport; +import static org.eclipse.jgit.lib.Constants.INFO_ALTERNATES; import static org.eclipse.jgit.lib.Constants.LOCK_SUFFIX; +import static org.eclipse.jgit.lib.Constants.OBJECTS; import java.io.BufferedReader; import java.io.FileNotFoundException; @@ -172,7 +174,7 @@ public class TransportSftp extends SshTransport implements WalkTransport { try { ftp = newSftp(); ftp.cd(path); - ftp.cd("objects"); //$NON-NLS-1$ + ftp.cd(OBJECTS); objectsPath = ftp.pwd(); } catch (FtpChannel.FtpException f) { throw new TransportException(MessageFormat.format( 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 9278f42adf..20b45882df 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -61,6 +61,7 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_DONE; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW; +import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_ALL; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK; @@ -92,6 +93,7 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.internal.storage.pack.CachedPackUriProvider; import org.eclipse.jgit.internal.storage.pack.PackWriter; import org.eclipse.jgit.internal.transport.parser.FirstWant; import org.eclipse.jgit.lib.BitmapIndex; @@ -113,6 +115,7 @@ import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevFlag; import org.eclipse.jgit.revwalk.RevFlagSet; import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.revwalk.RevSort; import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter; @@ -277,8 +280,6 @@ public class UploadPack { private PacketLineIn pckIn; - private PacketLineOut pckOut; - private OutputStream msgOut = NullOutputStream.INSTANCE; /** @@ -359,6 +360,8 @@ public class UploadPack { */ private FetchRequest currentRequest; + private CachedPackUriProvider cachedPackUriProvider; + /** * Create a new pack upload for an open repository. * @@ -602,6 +605,18 @@ public class UploadPack { } /** + * Get the currently installed protocol v2 hook. + * + * @return the hook or a default implementation if none installed. + * + * @since 5.5 + */ + public ProtocolV2Hook getProtocolV2Hook() { + return this.protocolV2Hook != null ? this.protocolV2Hook + : ProtocolV2Hook.DEFAULT; + } + + /** * Set the filter used while advertising the refs to the client. * <p> * Only refs allowed by this filter will be sent to the client. The filter @@ -724,6 +739,15 @@ public class UploadPack { this.clientRequestedV2 = params.contains("version=2"); //$NON-NLS-1$ } + /** + * @param p provider of URIs corresponding to cached packs (to support + * the packfile URIs feature) + * @since 5.5 + */ + public void setCachedPackUriProvider(@Nullable CachedPackUriProvider p) { + cachedPackUriProvider = p; + } + private boolean useProtocolV2() { return ProtocolVersion.V2.equals(transferConfig.protocolVersion) && clientRequestedV2; @@ -750,8 +774,9 @@ public class UploadPack { * other network connections this should be null. * @throws java.io.IOException */ - public void upload(final InputStream input, OutputStream output, - final OutputStream messages) throws IOException { + public void upload(InputStream input, OutputStream output, + @Nullable OutputStream messages) throws IOException { + PacketLineOut pckOut = null; try { rawIn = input; if (messages != null) @@ -777,10 +802,40 @@ public class UploadPack { pckIn = new PacketLineIn(rawIn); pckOut = new PacketLineOut(rawOut); if (useProtocolV2()) { - serviceV2(); + serviceV2(pckOut); } else { - service(); + service(pckOut); } + } catch (UploadPackInternalServerErrorException err) { + // UploadPackInternalServerErrorException is a special exception + // that indicates an error is already written to the client. Do + // nothing. + throw err; + } catch (ServiceMayNotContinueException err) { + if (!err.isOutput() && err.getMessage() != null && pckOut != null) { + try { + pckOut.writeString("ERR " + err.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (IOException e) { + err.addSuppressed(e); + throw err; + } + err.setOutput(); + } + throw err; + } catch (IOException | RuntimeException | Error err) { + if (pckOut != null) { + String msg = err instanceof PackProtocolException + ? err.getMessage() + : JGitText.get().internalServerError; + try { + pckOut.writeString("ERR " + msg + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (IOException e) { + err.addSuppressed(e); + throw err; + } + throw new UploadPackInternalServerErrorException(err); + } + throw err; } finally { msgOut = NullOutputStream.INSTANCE; walk.close(); @@ -941,7 +996,7 @@ public class UploadPack { return RefDatabase.findRef(getAdvertisedOrDefaultRefs(), name); } - private void service() throws IOException { + private void service(PacketLineOut pckOut) throws IOException { 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. @@ -997,7 +1052,7 @@ public class UploadPack { if (!req.getClientShallowCommits().isEmpty()) walk.assumeShallow(req.getClientShallowCommits()); - sendPack = negotiate(req, accumulator); + sendPack = negotiate(req, accumulator, pckOut); accumulator.timeNegotiating += System.currentTimeMillis() - negotiateStart; @@ -1012,31 +1067,6 @@ public class UploadPack { "\\x" + Integer.toHexString(eof))); //$NON-NLS-1$ } } - } catch (ServiceMayNotContinueException err) { - if (!err.isOutput() && err.getMessage() != null) { - try { - pckOut.writeString("ERR " + err.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ - err.setOutput(); - } catch (Throwable err2) { - // Ignore this secondary failure (and not mark output). - } - } - throw err; - } catch (IOException | RuntimeException | Error err) { - boolean output = false; - try { - String msg = err instanceof PackProtocolException - ? err.getMessage() - : JGitText.get().internalServerError; - pckOut.writeString("ERR " + msg + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ - output = true; - } catch (Throwable err2) { - // Ignore this secondary failure, leave output false. - } - if (output) { - throw new UploadPackInternalServerErrorException(err); - } - throw err; } finally { if (!sendPack && !biDirectionalPipe) { while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) { @@ -1048,11 +1078,11 @@ public class UploadPack { if (sendPack) { sendPack(accumulator, req, refs == null ? null : refs.values(), - unshallowCommits, Collections.emptyList()); + unshallowCommits, Collections.emptyList(), pckOut); } } - private void lsRefsV2() throws IOException { + private void lsRefsV2(PacketLineOut pckOut) throws IOException { ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig); LsRefsV2Request req = parser.parseLsRefsRequest(pckIn); protocolV2Hook.onLsRefs(req); @@ -1097,7 +1127,7 @@ public class UploadPack { return result; } - private void fetchV2() throws IOException { + private void fetchV2(PacketLineOut pckOut) throws IOException { // Depending on the requestValidator, #processHaveLines may // require that advertised be set. Set it only in the required // circumstances (to avoid a full ref lookup in the case that @@ -1117,6 +1147,10 @@ public class UploadPack { protocolV2Hook.onFetch(req); + if (req.getSidebandAll()) { + pckOut.setUsingSideband(true); + } + // TODO(ifrade): Refactor to pass around the Request object, instead of // copying data back to class fields List<ObjectId> deepenNots = new ArrayList<>(); @@ -1202,13 +1236,17 @@ public class UploadPack { if (sectionSent) pckOut.writeDelim(); - pckOut.writeString("packfile\n"); //$NON-NLS-1$ + if (!pckOut.isUsingSideband()) { + // sendPack will write "packfile\n" for us if sideband-all is used. + // But sideband-all is not used, so we have to write it ourselves. + pckOut.writeString("packfile\n"); //$NON-NLS-1$ + } sendPack(new PackStatistics.Accumulator(), req, req.getClientCapabilities().contains(OPTION_INCLUDE_TAG) ? db.getRefDatabase().getRefsByPrefix(R_TAGS) : null, - unshallowCommits, deepenNots); + unshallowCommits, deepenNots, pckOut); // sendPack invokes pckOut.end() for us, so we do not // need to invoke it here. } else { @@ -1221,7 +1259,7 @@ public class UploadPack { * Returns true if this is the last command and we should tear down the * connection. */ - private boolean serveOneCommandV2() throws IOException { + private boolean serveOneCommandV2(PacketLineOut pckOut) throws IOException { String command; try { command = pckIn.readString(); @@ -1236,34 +1274,38 @@ public class UploadPack { return true; } if (command.equals("command=" + COMMAND_LS_REFS)) { //$NON-NLS-1$ - lsRefsV2(); + lsRefsV2(pckOut); return false; } if (command.equals("command=" + COMMAND_FETCH)) { //$NON-NLS-1$ - fetchV2(); + fetchV2(pckOut); return false; } throw new PackProtocolException(MessageFormat .format(JGitText.get().unknownTransportCommand, command)); } + @SuppressWarnings("nls") private List<String> getV2CapabilityAdvertisement() { ArrayList<String> caps = new ArrayList<>(); - caps.add("version 2"); //$NON-NLS-1$ + caps.add("version 2"); caps.add(COMMAND_LS_REFS); - boolean advertiseRefInWant = transferConfig.isAllowRefInWant() && - db.getConfig().getBoolean("uploadpack", null, //$NON-NLS-1$ - "advertiserefinwant", true); //$NON-NLS-1$ - caps.add( - COMMAND_FETCH + '=' + - (transferConfig.isAllowFilter() ? OPTION_FILTER + ' ' : "") + //$NON-NLS-1$ - (advertiseRefInWant ? CAPABILITY_REF_IN_WANT + ' ' : "") + //$NON-NLS-1$ - OPTION_SHALLOW); + boolean advertiseRefInWant = transferConfig.isAllowRefInWant() + && db.getConfig().getBoolean("uploadpack", null, + "advertiserefinwant", true); + caps.add(COMMAND_FETCH + '=' + + (transferConfig.isAllowFilter() ? OPTION_FILTER + ' ' : "") + + (advertiseRefInWant ? CAPABILITY_REF_IN_WANT + ' ' : "") + + (transferConfig.isAllowSidebandAll() + ? OPTION_SIDEBAND_ALL + ' ' + : "") + + (cachedPackUriProvider != null ? "packfile-uris " : "") + + OPTION_SHALLOW); caps.add(CAPABILITY_SERVER_OPTION); return caps; } - private void serviceV2() throws IOException { + private void serviceV2(PacketLineOut pckOut) throws IOException { if (biDirectionalPipe) { // Just like in service(), the capability advertisement // is sent only if this is a bidirectional pipe. (If @@ -1276,14 +1318,14 @@ public class UploadPack { } pckOut.end(); - while (!serveOneCommandV2()) { + while (!serveOneCommandV2(pckOut)) { // Repeat until an empty command or EOF. } return; } try { - serveOneCommandV2(); + serveOneCommandV2(pckOut); } finally { while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) { // Discard until EOF. @@ -1579,7 +1621,8 @@ public class UploadPack { } private boolean negotiate(FetchRequest req, - PackStatistics.Accumulator accumulator) + PackStatistics.Accumulator accumulator, + PacketLineOut pckOut) throws IOException { okToGiveUp = Boolean.FALSE; @@ -1868,6 +1911,36 @@ public class UploadPack { } } + private static void checkReachabilityByWalkingObjects(ObjectWalk walk, + List<RevObject> wants, Set<ObjectId> reachableFrom) throws IOException { + + walk.sort(RevSort.TOPO); + for (RevObject want : wants) { + walk.markStart(want); + } + for (ObjectId have : reachableFrom) { + RevObject o = walk.parseAny(have); + walk.markUninteresting(o); + + RevObject peeled = walk.peel(o); + if (peeled instanceof RevCommit) { + // By default, for performance reasons, ObjectWalk does not mark a + // tree as uninteresting when we mark a commit. Mark it ourselves so + // that we can determine reachability exactly. + walk.markUninteresting(((RevCommit) peeled).getTree()); + } + } + + RevCommit commit = walk.next(); + if (commit != null) { + throw new WantNotValidException(commit); + } + RevObject object = walk.nextObject(); + if (object != null) { + throw new WantNotValidException(object); + } + } + private static void checkNotAdvertisedWants(UploadPack up, List<ObjectId> notAdvertisedWants, Set<ObjectId> reachableFrom) throws IOException { @@ -1888,6 +1961,17 @@ public class UploadPack { if (!allWantsAreCommits) { if (!repoHasBitmaps) { + if (up.transferConfig.isAllowFilter()) { + // Use allowFilter as an indication that the server + // operator is willing to pay the cost of these + // reachability checks. + try (ObjectWalk objWalk = walk.toObjectWalkWithSameObjects()) { + checkReachabilityByWalkingObjects(objWalk, + wantsAsObjs, reachableFrom); + } + return; + } + // If unadvertized non-commits are requested, use // bitmaps. If there are no bitmaps, instead of // incurring the expense of a manual walk, reject @@ -2016,6 +2100,8 @@ public class UploadPack { * shallow commits on the client that are now becoming unshallow * @param deepenNots * objects that the client specified using --shallow-exclude + * @param pckOut + * output writer * @throws IOException * if an error occurred while generating or writing the pack. */ @@ -2023,43 +2109,51 @@ public class UploadPack { FetchRequest req, @Nullable Collection<Ref> allTags, List<ObjectId> unshallowCommits, - List<ObjectId> deepenNots) throws IOException { + List<ObjectId> deepenNots, + PacketLineOut pckOut) throws IOException { Set<String> caps = req.getClientCapabilities(); boolean sideband = caps.contains(OPTION_SIDE_BAND) || caps.contains(OPTION_SIDE_BAND_64K); if (sideband) { try { sendPack(true, req, accumulator, allTags, unshallowCommits, - deepenNots); - } catch (ServiceMayNotContinueException noPack) { - // This was already reported on (below). - throw noPack; + deepenNots, pckOut); + } catch (ServiceMayNotContinueException err) { + String message = err.getMessage(); + if (message == null) { + message = JGitText.get().internalServerError; + } + try { + reportInternalServerErrorOverSideband(message); + } catch (IOException e) { + err.addSuppressed(e); + throw err; + } + throw new UploadPackInternalServerErrorException(err); } catch (IOException | RuntimeException | Error err) { - if (reportInternalServerErrorOverSideband()) { - throw new UploadPackInternalServerErrorException(err); - } else { + try { + reportInternalServerErrorOverSideband( + JGitText.get().internalServerError); + } catch (IOException e) { + err.addSuppressed(e); throw err; } + throw new UploadPackInternalServerErrorException(err); } } else { - sendPack(false, req, accumulator, allTags, unshallowCommits, deepenNots); + sendPack(false, req, accumulator, allTags, unshallowCommits, deepenNots, + pckOut); } } - private boolean reportInternalServerErrorOverSideband() { - try { - @SuppressWarnings("resource" /* java 7 */) - SideBandOutputStream err = new SideBandOutputStream( - SideBandOutputStream.CH_ERROR, - SideBandOutputStream.SMALL_BUF, - rawOut); - err.write(Constants.encode(JGitText.get().internalServerError)); - err.flush(); - return true; - } catch (Throwable cannotReport) { - // Ignore the reason. This is a secondary failure. - return false; - } + private void reportInternalServerErrorOverSideband(String message) + throws IOException { + @SuppressWarnings("resource" /* java 7 */) + SideBandOutputStream err = new SideBandOutputStream( + SideBandOutputStream.CH_ERROR, SideBandOutputStream.SMALL_BUF, + rawOut); + err.write(Constants.encode(message)); + err.flush(); } /** @@ -2079,6 +2173,8 @@ public class UploadPack { * shallow commits on the client that are now becoming unshallow * @param deepenNots * objects that the client specified using --shallow-exclude + * @param pckOut + * output writer * @throws IOException * if an error occurred while generating or writing the pack. */ @@ -2087,7 +2183,8 @@ public class UploadPack { PackStatistics.Accumulator accumulator, @Nullable Collection<Ref> allTags, List<ObjectId> unshallowCommits, - List<ObjectId> deepenNots) throws IOException { + List<ObjectId> deepenNots, + PacketLineOut pckOut) throws IOException { ProgressMonitor pm = NullProgressMonitor.INSTANCE; OutputStream packOut = rawOut; @@ -2105,25 +2202,12 @@ public class UploadPack { } } - try { - if (wantAll.isEmpty()) { - preUploadHook.onSendPack(this, wantIds, commonBase); - } else { - preUploadHook.onSendPack(this, wantAll, commonBase); - } - msgOut.flush(); - } catch (ServiceMayNotContinueException noPack) { - if (sideband && noPack.getMessage() != null) { - noPack.setOutput(); - @SuppressWarnings("resource" /* java 7 */) - SideBandOutputStream err = new SideBandOutputStream( - SideBandOutputStream.CH_ERROR, - SideBandOutputStream.SMALL_BUF, rawOut); - err.write(Constants.encode(noPack.getMessage())); - err.flush(); - } - throw noPack; + if (wantAll.isEmpty()) { + preUploadHook.onSendPack(this, wantIds, commonBase); + } else { + preUploadHook.onSendPack(this, wantAll, commonBase); } + msgOut.flush(); PackConfig cfg = packConfig; if (cfg == null) @@ -2219,12 +2303,32 @@ public class UploadPack { if (peeledId == null || objectId == null) continue; + objectId = ref.getObjectId(); if (pw.willInclude(peeledId) && !pw.willInclude(objectId)) { - pw.addObject(rw.parseAny(objectId)); + RevObject o = rw.parseAny(objectId); + addTagChain(o, pw); + pw.addObject(o); } } } + if (pckOut.isUsingSideband()) { + if (req instanceof FetchV2Request && + cachedPackUriProvider != null && + !((FetchV2Request) req).getPackfileUriProtocols().isEmpty()) { + FetchV2Request reqV2 = (FetchV2Request) req; + pw.setPackfileUriConfig(new PackWriter.PackfileUriConfig( + pckOut, + reqV2.getPackfileUriProtocols(), + cachedPackUriProvider)); + } else { + // PackWriter will write "packfile-uris\n" and "packfile\n" + // for us if provided a PackfileUriConfig. In this case, we + // are not providing a PackfileUriConfig, so we have to + // write this line ourselves. + pckOut.writeString("packfile\n"); //$NON-NLS-1$ + } + } pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut); if (msgOut != NullOutputStream.INSTANCE) { @@ -2253,6 +2357,18 @@ public class UploadPack { } } + private void addTagChain( + RevObject o, PackWriter pw) throws IOException { + while (Constants.OBJ_TAG == o.getType()) { + RevTag t = (RevTag) o; + o = t.getObject(); + if (o.getType() == Constants.OBJ_TAG && !pw.willInclude(o.getId())) { + walk.parseBody(o); + pw.addObject(o); + } + } + } + private static class ResponseBufferedOutputStream extends OutputStream { private final OutputStream rawOut; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java index b4248ee3fc..7a973aff34 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java @@ -66,8 +66,8 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; import javax.crypto.spec.SecretKeySpec; -import javax.xml.bind.DatatypeConverter; +import org.bouncycastle.util.encoders.Hex; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.util.Base64; @@ -301,7 +301,7 @@ abstract class WalkEncryption { String DEFAULT_KEY_ALGO = JetS3tV2.ALGORITHM; String DEFAULT_KEY_SIZE = Integer.toString(JetS3tV2.KEY_SIZE); String DEFAULT_KEY_ITER = Integer.toString(JetS3tV2.ITERATIONS); - String DEFAULT_KEY_SALT = DatatypeConverter.printHexBinary(JetS3tV2.SALT); + String DEFAULT_KEY_SALT = Hex.toHexString(JetS3tV2.SALT); String EMPTY = ""; //$NON-NLS-1$ @@ -377,8 +377,7 @@ abstract class WalkEncryption { final byte[] salt; try { - salt = DatatypeConverter - .parseHexBinary(keySalt.replaceAll(REGEX_WS, EMPTY)); + salt = Hex.decode(keySalt.replaceAll(REGEX_WS, EMPTY)); } catch (Exception e) { throw securityError(X_KEY_SALT + EMPTY + keySalt); } 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 5c67253cfc..4862d676e1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java @@ -82,10 +82,6 @@ abstract class WalkRemoteObjectDatabase { static final String INFO_PACKS = "info/packs"; //$NON-NLS-1$ - static final String INFO_ALTERNATES = "info/alternates"; //$NON-NLS-1$ - - static final String INFO_HTTP_ALTERNATES = "info/http-alternates"; //$NON-NLS-1$ - static final String INFO_REFS = ROOT_DIR + Constants.INFO_REFS; abstract URIish getURI(); @@ -107,8 +103,10 @@ abstract class WalkRemoteObjectDatabase { /** * Obtain alternate connections to alternate object databases (if any). * <p> - * Alternates are typically read from the file {@link #INFO_ALTERNATES} or - * {@link #INFO_HTTP_ALTERNATES}. The content of each line must be resolved + * Alternates are typically read from the file + * {@link org.eclipse.jgit.lib.Constants#INFO_ALTERNATES} or + * {@link org.eclipse.jgit.lib.Constants#INFO_HTTP_ALTERNATES}. + * The content of each line must be resolved * by the implementation and a new database reference should be returned to * represent the additional location. * <p> 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 90305013f5..29519298c4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -64,6 +64,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; +import java.security.AccessControlException; import java.security.AccessController; import java.security.PrivilegedAction; import java.text.MessageFormat; @@ -122,6 +123,8 @@ public abstract class FS { */ protected static final Entry[] NO_ENTRIES = {}; + private volatile Boolean supportSymlinks; + /** * This class creates FS instances. It will be overridden by a Java7 variant * if such can be detected in {@link #detect(Boolean)}. @@ -276,15 +279,19 @@ public abstract class FS { * @return FileStoreAttributes for the given path. */ public static FileStoreAttributes get(Path path) { - path = path.toAbsolutePath(); - Path dir = Files.isDirectory(path) ? path : path.getParent(); - FileStoreAttributes cached = attrCacheByPath.get(dir); - if (cached != null) { - return cached; + try { + path = path.toAbsolutePath(); + Path dir = Files.isDirectory(path) ? path : path.getParent(); + FileStoreAttributes cached = attrCacheByPath.get(dir); + if (cached != null) { + return cached; + } + FileStoreAttributes attrs = getFileStoreAttributes(dir); + attrCacheByPath.put(dir, attrs); + return attrs; + } catch (SecurityException e) { + return FALLBACK_FILESTORE_ATTRIBUTES; } - FileStoreAttributes attrs = getFileStoreAttributes(dir); - attrCacheByPath.put(dir, attrs); - return attrs; } private static FileStoreAttributes getFileStoreAttributes(Path dir) { @@ -813,7 +820,32 @@ public abstract class FS { * @since 3.0 */ public boolean supportsSymlinks() { - return false; + if (supportSymlinks == null) { + detectSymlinkSupport(); + } + return Boolean.TRUE.equals(supportSymlinks); + } + + private void detectSymlinkSupport() { + File tempFile = null; + try { + tempFile = File.createTempFile("tempsymlinktarget", ""); //$NON-NLS-1$ //$NON-NLS-2$ + File linkName = new File(tempFile.getParentFile(), "tempsymlink"); //$NON-NLS-1$ + createSymLink(linkName, tempFile.getPath()); + supportSymlinks = Boolean.TRUE; + linkName.delete(); + } catch (IOException | UnsupportedOperationException | SecurityException + | InternalError e) { + supportSymlinks = Boolean.FALSE; + } finally { + if (tempFile != null) { + try { + FileUtils.delete(tempFile); + } catch (IOException e) { + throw new RuntimeException(e); // panic + } + } + } } /** @@ -1067,9 +1099,16 @@ public abstract class FS { for (String p : path.split(File.pathSeparator)) { for (String command : lookFor) { - final File e = new File(p, command); - if (e.isFile()) - return e.getAbsoluteFile(); + final File file = new File(p, command); + try { + if (file.isFile()) { + return file.getAbsoluteFile(); + } + } catch (SecurityException e) { + LOG.warn(MessageFormat.format( + JGitText.get().skipNotAccessiblePath, + file.getPath())); + } } } return null; @@ -1172,6 +1211,13 @@ public abstract class FS { } } catch (IOException e) { LOG.error("Caught exception in FS.readPipe()", e); //$NON-NLS-1$ + } catch (AccessControlException e) { + LOG.warn(MessageFormat.format( + JGitText.get().readPipeIsNotAllowedRequiredPermission, + command, dir, e.getPermission())); + } catch (SecurityException e) { + LOG.warn(MessageFormat.format(JGitText.get().readPipeIsNotAllowed, + command, dir)); } if (debug) { LOG.debug("readpipe returns null"); //$NON-NLS-1$ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java index a485389a9a..6a1eef2d66 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java @@ -287,12 +287,6 @@ public class FS_POSIX extends FS { /** {@inheritDoc} */ @Override - public boolean supportsSymlinks() { - return true; - } - - /** {@inheritDoc} */ - @Override public void setHidden(File path, boolean hidden) throws IOException { // no action on POSIX } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java index 7fe80bb21a..1e64a38bb1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java @@ -74,8 +74,6 @@ import org.slf4j.LoggerFactory; public class FS_Win32 extends FS { private final static Logger LOG = LoggerFactory.getLogger(FS_Win32.class); - private volatile Boolean supportSymlinks; - /** * Constructor */ @@ -239,37 +237,6 @@ public class FS_Win32 extends FS { /** {@inheritDoc} */ @Override - public boolean supportsSymlinks() { - if (supportSymlinks == null) { - detectSymlinkSupport(); - } - return Boolean.TRUE.equals(supportSymlinks); - } - - private void detectSymlinkSupport() { - File tempFile = null; - try { - tempFile = File.createTempFile("tempsymlinktarget", ""); //$NON-NLS-1$ //$NON-NLS-2$ - File linkName = new File(tempFile.getParentFile(), "tempsymlink"); //$NON-NLS-1$ - createSymLink(linkName, tempFile.getPath()); - supportSymlinks = Boolean.TRUE; - linkName.delete(); - } catch (IOException | UnsupportedOperationException - | InternalError e) { - supportSymlinks = Boolean.FALSE; - } finally { - if (tempFile != null) { - try { - FileUtils.delete(tempFile); - } catch (IOException e) { - throw new RuntimeException(e); // panic - } - } - } - } - - /** {@inheritDoc} */ - @Override public Attributes getAttributes(File path) { return FileUtils.getFileAttributesBasic(this, path); } |