diff options
Diffstat (limited to 'org.eclipse.jgit')
164 files changed, 3557 insertions, 1228 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index a0ee918d46..8c33e9eb7f 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -90,33 +90,27 @@ </message_arguments> </filter> </resource> - <resource path="src/org/eclipse/jgit/lib/ObjectIdRef.java" type="org.eclipse.jgit.lib.ObjectIdRef"> - <filter id="338722907"> - <message_arguments> - <message_argument value="org.eclipse.jgit.lib.ObjectIdRef"/> - <message_argument value="ObjectIdRef(Ref.Storage, String, ObjectId)"/> - </message_arguments> - </filter> - </resource> <resource path="src/org/eclipse/jgit/lib/Ref.java" type="org.eclipse.jgit.lib.Ref"> - <filter id="404000815"> + <filter id="403767336"> <message_arguments> <message_argument value="org.eclipse.jgit.lib.Ref"/> - <message_argument value="getUpdateIndex()"/> + <message_argument value="UNDEFINED_UPDATE_INDEX"/> </message_arguments> </filter> </resource> - <resource path="src/org/eclipse/jgit/lib/RefDatabase.java" type="org.eclipse.jgit.lib.RefDatabase"> - <filter id="421650549"> + <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.RefDatabase"/> - <message_argument value="exactRef(String)"/> + <message_argument value="org.eclipse.jgit.lib.Repository"/> + <message_argument value="getIdentifier()"/> </message_arguments> </filter> - <filter id="421654647"> + </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.lib.RefDatabase"/> - <message_argument value="getRef(String)"/> + <message_argument value="org.eclipse.jgit.revwalk.ObjectWalk"/> + <message_argument value="SIMPLE_VISITATION_POLICY"/> </message_arguments> </filter> </resource> @@ -199,55 +193,53 @@ </message_arguments> </filter> </resource> - <resource path="src/org/eclipse/jgit/transport/BaseReceivePack.java" type="org.eclipse.jgit.transport.BaseReceivePack"> - <filter id="421650549"> + <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.transport.BaseReceivePack"/> - <message_argument value="getAdvertisedRefs()"/> + <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/> + <message_argument value="treesTraversed"/> </message_arguments> </filter> - <filter id="421650549"> + </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.BaseReceivePack"/> - <message_argument value="getPushCertificate()"/> + <message_argument value="org.eclipse.jgit.transport.HttpConfig"/> + <message_argument value="COOKIE_FILE_CACHE_LIMIT_KEY"/> </message_arguments> </filter> - <filter id="421650549"> + <filter id="336658481"> <message_arguments> - <message_argument value="org.eclipse.jgit.transport.BaseReceivePack"/> - <message_argument value="getRepository()"/> + <message_argument value="org.eclipse.jgit.transport.HttpConfig"/> + <message_argument value="COOKIE_FILE_KEY"/> </message_arguments> </filter> - <filter id="421650549"> + <filter id="336658481"> <message_arguments> - <message_argument value="org.eclipse.jgit.transport.BaseReceivePack"/> - <message_argument value="getRevWalk()"/> + <message_argument value="org.eclipse.jgit.transport.HttpConfig"/> + <message_argument value="SAVE_COOKIES_KEY"/> </message_arguments> </filter> - <filter id="421650549"> + </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.BaseReceivePack"/> - <message_argument value="setAdvertisedRefs(Map<String,Ref>, Set<ObjectId>)"/> + <message_argument value="org.eclipse.jgit.transport.Transport"/> + <message_argument value="getFilterBlobLimit()"/> </message_arguments> </filter> - <filter id="421650549"> + <filter id="421654647"> <message_arguments> - <message_argument value="org.eclipse.jgit.transport.BaseReceivePack"/> - <message_argument value="setPushCertificate(PushCertificate)"/> + <message_argument value="org.eclipse.jgit.transport.Transport"/> + <message_argument value="setFilterBlobLimit(long)"/> </message_arguments> </filter> </resource> - <resource path="src/org/eclipse/jgit/transport/TransferConfig.java" type="org.eclipse.jgit.transport.TransferConfig"> - <filter id="1159725059"> - <message_arguments> - <message_argument value="5.1.4"/> - <message_argument value="TransferConfig(Config)"/> - </message_arguments> - </filter> - <filter id="1159725059"> + <resource path="src/org/eclipse/jgit/transport/UploadPack.java" type="org.eclipse.jgit.transport.UploadPack"> + <filter id="421654647"> <message_arguments> - <message_argument value="5.1.4"/> - <message_argument value="TransferConfig(Repository)"/> + <message_argument value="org.eclipse.jgit.transport.UploadPack"/> + <message_argument value="getFilterBlobLimit()"/> </message_arguments> </filter> </resource> @@ -276,12 +268,6 @@ <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS"> <filter id="1142947843"> <message_arguments> - <message_argument value="4.5.6"/> - <message_argument value="fileAttributes(File)"/> - </message_arguments> - </filter> - <filter id="1142947843"> - <message_arguments> <message_argument value="5.1.9"/> <message_argument value="getFileStoreAttributes(Path)"/> </message_arguments> @@ -335,6 +321,26 @@ </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"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.HttpSupport"/> + <message_argument value="HDR_SET_COOKIE2"/> + </message_arguments> + </filter> + </resource> <resource path="src/org/eclipse/jgit/util/Monitoring.java" type="org.eclipse.jgit.util.Monitoring"> <filter id="1109393411"> <message_arguments> diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index 520b20b244..5bfa344c48 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -3,12 +3,12 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit Bundle-SymbolicName: org.eclipse.jgit -Bundle-Version: 5.3.8.qualifier +Bundle-Version: 5.4.4.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy -Export-Package: org.eclipse.jgit.annotations;version="5.3.8", - org.eclipse.jgit.api;version="5.3.8"; +Export-Package: org.eclipse.jgit.annotations;version="5.4.4", + org.eclipse.jgit.api;version="5.4.4"; 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.3.8", org.eclipse.jgit.submodule, org.eclipse.jgit.transport, org.eclipse.jgit.merge", - org.eclipse.jgit.api.errors;version="5.3.8";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors", - org.eclipse.jgit.attributes;version="5.3.8", - org.eclipse.jgit.blame;version="5.3.8"; + 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"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff", - org.eclipse.jgit.diff;version="5.3.8"; + org.eclipse.jgit.diff;version="5.4.4"; 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.3.8"; + org.eclipse.jgit.dircache;version="5.4.4"; 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.3.8"; + org.eclipse.jgit.errors;version="5.4.4"; 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.3.8";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.fnmatch;version="5.3.8", - org.eclipse.jgit.gitrepo;version="5.3.8"; + 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"; 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.3.8";x-internal:=true, - org.eclipse.jgit.hooks;version="5.3.8";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.ignore;version="5.3.8", - org.eclipse.jgit.ignore.internal;version="5.3.8";x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal;version="5.3.8";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", - org.eclipse.jgit.internal.fsck;version="5.3.8";x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.ketch;version="5.3.8";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.revwalk;version="5.3.8";x-internal:=true, - org.eclipse.jgit.internal.storage.dfs;version="5.3.8"; + 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"; 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.3.8"; + org.eclipse.jgit.internal.storage.file;version="5.4.4"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.junit, org.eclipse.jgit.junit.http, @@ -77,18 +77,19 @@ Export-Package: org.eclipse.jgit.annotations;version="5.3.8", org.eclipse.jgit.pgm, org.eclipse.jgit.pgm.test, org.eclipse.jgit.ssh.apache", - org.eclipse.jgit.internal.storage.io;version="5.3.8";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.pack;version="5.3.8";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.reftable;version="5.3.8"; + 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"; 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.3.8";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.submodule;version="5.3.8";x-internal:=true, - org.eclipse.jgit.internal.transport.parser;version="5.3.8";x-friends:="org.eclipse.jgit.http.server,org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.ssh;version="5.3.8";x-friends:="org.eclipse.jgit.ssh.apache", - org.eclipse.jgit.lib;version="5.3.8"; + 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"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util, @@ -98,33 +99,33 @@ Export-Package: org.eclipse.jgit.annotations;version="5.3.8", org.eclipse.jgit.treewalk, org.eclipse.jgit.transport, org.eclipse.jgit.submodule", - org.eclipse.jgit.lib.internal;version="5.3.8";x-internal:=true, - org.eclipse.jgit.merge;version="5.3.8"; + org.eclipse.jgit.lib.internal;version="5.4.4";x-internal:=true, + org.eclipse.jgit.merge;version="5.4.4"; 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.3.8", - org.eclipse.jgit.notes;version="5.3.8"; + org.eclipse.jgit.nls;version="5.4.4", + org.eclipse.jgit.notes;version="5.4.4"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.merge", - org.eclipse.jgit.patch;version="5.3.8";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff", - org.eclipse.jgit.revplot;version="5.3.8";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk", - org.eclipse.jgit.revwalk;version="5.3.8"; + 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"; 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.3.8";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util", - org.eclipse.jgit.storage.file;version="5.3.8";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util", - org.eclipse.jgit.storage.pack;version="5.3.8";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.submodule;version="5.3.8";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk", - org.eclipse.jgit.transport;version="5.3.8"; + 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"; uses:="org.eclipse.jgit.transport.resolver, org.eclipse.jgit.revwalk, org.eclipse.jgit.internal.storage.pack, @@ -137,39 +138,40 @@ Export-Package: org.eclipse.jgit.annotations;version="5.3.8", org.eclipse.jgit.transport.http, org.eclipse.jgit.errors, org.eclipse.jgit.storage.pack", - org.eclipse.jgit.transport.http;version="5.3.8";uses:="javax.net.ssl", - org.eclipse.jgit.transport.resolver;version="5.3.8";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport", - org.eclipse.jgit.treewalk;version="5.3.8"; + 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"; 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.3.8";uses:="org.eclipse.jgit.treewalk", - org.eclipse.jgit.util;version="5.3.8"; + org.eclipse.jgit.treewalk.filter;version="5.4.4";uses:="org.eclipse.jgit.treewalk", + org.eclipse.jgit.util;version="5.4.4"; 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.3.8", - org.eclipse.jgit.util.sha1;version="5.3.8", - org.eclipse.jgit.util.time;version="5.3.8" + 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" 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)", javax.crypto, javax.net.ssl, - org.bouncycastle;version="[1.60.0,2.0.0)", - org.bouncycastle.bcpg;version="[1.60.0,2.0.0)", - org.bouncycastle.gpg;version="[1.60.0,2.0.0)", - org.bouncycastle.gpg.keybox;version="[1.60.0,2.0.0)", - org.bouncycastle.jce.provider;version="[1.60.0,2.0.0)", - org.bouncycastle.openpgp;version="[1.60.0,2.0.0)", - org.bouncycastle.openpgp.jcajce;version="[1.60.0,2.0.0)", - org.bouncycastle.openpgp.operator;version="[1.60.0,2.0.0)", - org.bouncycastle.openpgp.operator.jcajce;version="[1.60.0,2.0.0)", - org.bouncycastle.util.encoders;version="[1.60.0,2.0.0)", + org.bouncycastle;version="[1.61.0,2.0.0)", + org.bouncycastle.bcpg;version="[1.61.0,2.0.0)", + org.bouncycastle.gpg;version="[1.61.0,2.0.0)", + org.bouncycastle.gpg.keybox;version="[1.61.0,2.0.0)", + org.bouncycastle.gpg.keybox.jcajce;version="[1.61.0,2.0.0)", + org.bouncycastle.jce.provider;version="[1.61.0,2.0.0)", + org.bouncycastle.openpgp;version="[1.61.0,2.0.0)", + org.bouncycastle.openpgp.jcajce;version="[1.61.0,2.0.0)", + org.bouncycastle.openpgp.operator;version="[1.61.0,2.0.0)", + org.bouncycastle.openpgp.operator.jcajce;version="[1.61.0,2.0.0)", + org.bouncycastle.util.encoders;version="[1.61.0,2.0.0)", org.slf4j;version="[1.7.0,2.0.0)", org.xml.sax, org.xml.sax.helpers diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF index bdcb53f052..c3d522448e 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.3.8.qualifier -Eclipse-SourceBundle: org.eclipse.jgit;version="5.3.8.qualifier";roots="." +Bundle-Version: 5.4.4.qualifier +Eclipse-SourceBundle: org.eclipse.jgit;version="5.4.4.qualifier";roots="." diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index 84b281f082..7576f3765a 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.3.8-SNAPSHOT</version> + <version>5.4.4-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit</artifactId> @@ -115,7 +115,7 @@ <includes> <include>plugin.properties</include> <include>about.html</include> - <include>META-INF/eclipse.inf</include> + <include>META-INF/eclipse.inf</include> </includes> </resource> <resource> 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 43946265ce..26b2580693 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -51,7 +51,7 @@ cannotAccessLastModifiedForSafeDeletion=Unable to access lastModifiedTime of fil cannotBeCombined=Cannot be combined. cannotBeRecursiveWhenTreesAreIncluded=TreeWalk shouldn't be recursive when tree objects are included. cannotChangeActionOnComment=Cannot change action on comment line in git-rebase-todo file, old action: {0}, new action: {1}. -cannotCheckoutFromUnbornBranch=Cannot checkout from unborn branch +cannotCheckoutFromUnbornBranch=Cannot check out from unborn branch cannotCheckoutOursSwitchBranch=Checking out ours/theirs is only possible when checking out index, not when switching branches. cannotCombineSquashWithNoff=Cannot combine --squash with --no-ff. cannotCombineTreeFilterWithRevFilter=Cannot combine TreeFilter {0} with RevFilter {1}. @@ -130,6 +130,7 @@ configSubsectionContainsNewline=config subsection name contains newline configSubsectionContainsNullByte=config subsection name contains byte 0x00 configValueContainsNullByte=config value contains byte 0x00 configHandleIsStale=config file handle is stale, {0}. retry +configHandleMayBeLocked=config file handle may be locked by other process, {0}. retry connectionFailed=connection failed connectionTimeOut=Connection time out: {0} contextMustBeNonNegative=context must be >= 0 @@ -162,8 +163,8 @@ corruptObjectInvalidTree=invalid tree corruptObjectInvalidType=invalid type corruptObjectInvalidType2=invalid type {0} corruptObjectMissingEmail=missing email -corruptObjectNameContainsByte=name contains byte 0x%x -corruptObjectNameContainsChar=name contains '%c' +corruptObjectNameContainsByte=byte 0x%x not allowed in Windows filename +corruptObjectNameContainsChar=char '%c' not allowed in Windows filename corruptObjectNameContainsNullByte=name contains byte 0x00 corruptObjectNameContainsSlash=name contains '/' corruptObjectNameDot=invalid name '.' @@ -185,7 +186,12 @@ corruptObjectZeroId=entry points to null SHA-1 corruptUseCnt=close() called when useCnt is already zero for {0} couldNotGetAdvertisedRef=Remote {0} did not advertise Ref for branch {1}. This Ref may not exist in the remote or may be hidden by permission settings. couldNotGetRepoStatistics=Could not get repository statistics +couldNotFindTabInLine=Could not find tab in line {0}. Tab is the mandatory separator for the Netscape Cookie File Format. +couldNotFindSixTabsInLine=Could not find 6 tabs but only {0} in line '{1}'. 7 tab separated columns per line are mandatory for the Netscape Cookie File Format. couldNotLockHEAD=Could not lock HEAD +couldNotPersistCookies=Could not persist received cookies in file ''{0}'' +couldNotReadCookieFile=Could not read cookie file ''{0}'' +couldNotReadIndexInOneGo=Could not read index in one go, only {0} out of {1} read couldNotReadObjectWhileParsingCommit=Could not read an object while parsing commit {0} couldNotRewindToUpstreamCommit=Could not rewind to upstream commit couldNotURLEncodeToUTF8=Could not URL encode to UTF-8 @@ -296,6 +302,7 @@ gpgNoKeyring=neither pubring.kbx nor secring.gpg files found gpgNoKeyInLegacySecring=no matching secret key found in legacy secring.gpg for key or user id: {0} gpgNoPublicKeyFound=Unable to find a public-key with key or user id: {0} gpgNoSecretKeyForPublicKey=unable to find associated secret key for public key: {0} +gpgNotASigningKey=Secret key ({0}) is not suitable for signing gpgKeyInfo=GPG Key (fingerprint {0}) gpgSigningCancelled=Signing was cancelled headRequiredToStash=HEAD required to stash local changes @@ -411,6 +418,7 @@ mismatchOffset=mismatch offset for object {0} mismatchCRC=mismatch CRC for object {0} missingAccesskey=Missing accesskey. missingConfigurationForKey=No value for key {0} found in configuration +missingCookieFile=Configured http.cookieFile ''{0}'' is missing missingCRC=missing CRC for object {0} missingDeltaBase=delta base missingForwardImageInGITBinaryPatch=Missing forward-image in GIT binary patch @@ -575,6 +583,7 @@ rewinding=Rewinding to commit {0} s3ActionDeletion=Deletion s3ActionReading=Reading s3ActionWriting=Writing +searchForReachableBranches=Finding reachable branches saveFileStoreAttributesFailed=Saving measured FileStore attributes to user config failed searchForReuse=Finding sources searchForSizes=Getting sizes diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java index 65b72f7d95..c9dd547b49 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java @@ -157,8 +157,8 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> { merger.setCommitNames(new String[] { "BASE", ourName, //$NON-NLS-1$ cherryPickName }); if (merger.merge(newHead, srcCommit)) { - if (AnyObjectId.equals(newHead.getTree().getId(), merger - .getResultTreeId())) + if (AnyObjectId.isEqual(newHead.getTree().getId(), + merger.getResultTreeId())) continue; DirCacheCheckout dco = new DirCacheCheckout(repo, newHead.getTree(), repo.lockDirCache(), diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java index 61d51cc913..b55987ead4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -282,7 +282,7 @@ public class CommitCommand extends GitCommand<RevCommit> { ru.setRefLogMessage(reflogComment, false); } else { String prefix = amend ? "commit (amend): " //$NON-NLS-1$ - : parents.size() == 0 ? "commit (initial): " //$NON-NLS-1$ + : parents.isEmpty() ? "commit (initial): " //$NON-NLS-1$ : "commit: "; //$NON-NLS-1$ ru.setRefLogMessage(prefix + revCommit.getShortMessage(), false); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java index 31e7281d64..c3feaeffca 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java @@ -46,6 +46,7 @@ package org.eclipse.jgit.api; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -188,8 +189,7 @@ public class DeleteBranchCommand extends GitCommand<List<String>> { public DeleteBranchCommand setBranchNames(String... branchnames) { checkCallable(); this.branchNames.clear(); - for (String branch : branchnames) - this.branchNames.add(branch); + this.branchNames.addAll(Arrays.asList(branchnames)); return this; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteTagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteTagCommand.java index 63a090cafc..1970240621 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteTagCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteTagCommand.java @@ -45,6 +45,7 @@ package org.eclipse.jgit.api; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -135,8 +136,7 @@ public class DeleteTagCommand extends GitCommand<List<String>> { public DeleteTagCommand setTags(String... tags) { checkCallable(); this.tags.clear(); - for (String tagName : tags) - this.tags.add(tagName); + this.tags.addAll(Arrays.asList(tags)); return this; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java index a484742e08..9ad77e65fd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java @@ -63,8 +63,7 @@ import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.InvalidPatternException; import org.eclipse.jgit.errors.MissingObjectException; -import org.eclipse.jgit.ignore.internal.IMatcher; -import org.eclipse.jgit.ignore.internal.PathMatcher; +import org.eclipse.jgit.fnmatch.FileNameMatcher; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; @@ -102,14 +101,19 @@ public class DescribeCommand extends GitCommand<String> { private boolean longDesc; /** - * Pattern matchers to be applied to tags under consideration + * Pattern matchers to be applied to tags under consideration. */ - private List<IMatcher> matchers = new ArrayList<>(); + private List<FileNameMatcher> matchers = new ArrayList<>(); /** - * Whether to use all tags (incl. lightweight) or not + * Whether to use all tags (incl. lightweight) or not. */ - private boolean useTags = false; + private boolean useTags; + + /** + * Whether to show a uniquely abbreviated commit hash as a fallback or not. + */ + private boolean always; /** * Constructor for DescribeCommand. @@ -197,6 +201,21 @@ public class DescribeCommand extends GitCommand<String> { return this; } + /** + * Always describe the commit by eventually falling back to a uniquely + * abbreviated commit hash if no other name matches. + * + * @param always + * <code>true</code> enables falling back to a uniquely + * abbreviated commit hash + * @return {@code this} + * @since 5.4 + */ + public DescribeCommand setAlways(boolean always) { + this.always = always; + return this; + } + private String longDescription(Ref tag, int depth, ObjectId tip) throws IOException { return String.format( @@ -222,7 +241,7 @@ public class DescribeCommand extends GitCommand<String> { */ public DescribeCommand setMatch(String... patterns) throws InvalidPatternException { for (String p : patterns) { - matchers.add(PathMatcher.createPathMatcher(p, null, false)); + matchers.add(new FileNameMatcher(p, null)); } return this; } @@ -246,18 +265,24 @@ public class DescribeCommand extends GitCommand<String> { }; private Optional<Ref> getBestMatch(List<Ref> tags) { - if (tags == null || tags.size() == 0) { + if (tags == null || tags.isEmpty()) { return Optional.empty(); - } else if (matchers.size() == 0) { + } else if (matchers.isEmpty()) { Collections.sort(tags, TAG_TIE_BREAKER); return Optional.of(tags.get(0)); } else { // Find the first tag that matches in the stream of all tags // filtered by matchers ordered by tie break order Stream<Ref> matchingTags = Stream.empty(); - for (IMatcher matcher : matchers) { + for (FileNameMatcher matcher : matchers) { Stream<Ref> m = tags.stream().filter( - tag -> matcher.matches(tag.getName(), false, false)); + tag -> { + matcher.append( + tag.getName().substring(R_TAGS.length())); + boolean result = matcher.isMatch(); + matcher.reset(); + return result; + }); matchingTags = Stream.of(matchingTags, m).flatMap(i -> i); } return matchingTags.sorted(TAG_TIE_BREAKER).findFirst(); @@ -399,15 +424,12 @@ public class DescribeCommand extends GitCommand<String> { } // if all the nodes are dominated by all the tags, the walk stops - if (candidates.isEmpty()) - return null; + if (candidates.isEmpty()) { + return always ? w.getObjectReader().abbreviate(target).name() : null; + } - Candidate best = Collections.min(candidates, new Comparator<Candidate>() { - @Override - public int compare(Candidate o1, Candidate o2) { - return o1.depth - o2.depth; - } - }); + Candidate best = Collections.min(candidates, + (Candidate o1, Candidate o2) -> o1.depth - o2.depth); return best.describe(target); } catch (IOException e) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java index 400a7dfe48..835e7b5fd2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java @@ -43,6 +43,8 @@ */ package org.eclipse.jgit.api; +import static java.util.Objects.requireNonNull; + import java.io.File; import java.io.IOException; @@ -220,9 +222,7 @@ public class Git implements AutoCloseable { } Git(Repository repo, boolean closeRepo) { - if (repo == null) - throw new NullPointerException(); - this.repo = repo; + this.repo = requireNonNull(repo); this.closeRepo = closeRepo; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java index 29a51a0f02..e0eafc7533 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java @@ -53,7 +53,6 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.List; import org.eclipse.jgit.api.errors.GitAPIException; @@ -134,12 +133,8 @@ public class ListBranchCommand extends GitCommand<List<Ref>> { throw new JGitInternalException(e.getMessage(), e); } - Collections.sort(resultRefs, new Comparator<Ref>() { - @Override - public int compare(Ref o1, Ref o2) { - return o1.getName().compareTo(o2.getName()); - } - }); + Collections.sort(resultRefs, + (Ref o1, Ref o2) -> o1.getName().compareTo(o2.getName())); setCallable(false); return resultRefs; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java index 01c1991846..c894d056a0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java @@ -45,7 +45,6 @@ package org.eclipse.jgit.api; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.List; import org.eclipse.jgit.api.errors.GitAPIException; @@ -87,12 +86,8 @@ public class ListTagCommand extends GitCommand<List<Ref>> { } catch (IOException e) { throw new JGitInternalException(e.getMessage(), e); } - Collections.sort(tags, new Comparator<Ref>() { - @Override - public int compare(Ref o1, Ref o2) { - return o1.getName().compareTo(o2.getName()); - } - }); + Collections.sort(tags, + (Ref o1, Ref o2) -> o1.getName().compareTo(o2.getName())); setCallable(false); return tags; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java index cf3d35fe89..66de8ae131 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java @@ -133,7 +133,7 @@ public class LogCommand extends GitCommand<Iterable<RevCommit>> { @Override public Iterable<RevCommit> call() throws GitAPIException, NoHeadException { checkCallable(); - if (pathFilters.size() > 0) + if (!pathFilters.isEmpty()) walk.setTreeFilter(AndTreeFilter.create( PathFilterGroup.create(pathFilters), TreeFilter.ANY_DIFF)); if (skip > -1 && maxCount > -1) @@ -282,13 +282,11 @@ public class LogCommand extends GitCommand<Iterable<RevCommit>> { RevCommit commit = null; try { commit = walk.parseCommit(objectId); - } catch (MissingObjectException e) { - // ignore: the ref points to an object that does not exist; - // it should be ignored as traversal starting point. - } catch (IncorrectObjectTypeException e) { - // ignore: the ref points to an object that is not a commit - // (e.g. a tree or a blob); - // it should be ignored as traversal starting point. + } catch (MissingObjectException | IncorrectObjectTypeException e) { + // ignore as traversal starting point: + // - the ref points to an object that does not exist + // - the ref points to an object that is not a commit (e.g. a + // tree or a blob) } if (commit != null) add(commit); @@ -348,9 +346,7 @@ public class LogCommand extends GitCommand<Iterable<RevCommit>> { } else walk.markUninteresting(walk.lookupCommit(start)); return this; - } catch (MissingObjectException e) { - throw e; - } catch (IncorrectObjectTypeException e) { + } catch (MissingObjectException | IncorrectObjectTypeException e) { throw e; } catch (IOException e) { throw new JGitInternalException(MessageFormat.format( diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java index f0ad29db49..bdb2d1bbc5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java @@ -60,6 +60,7 @@ import org.eclipse.jgit.api.errors.NoHeadException; import org.eclipse.jgit.api.errors.RefNotAdvertisedException; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.api.errors.WrongRepositoryStateException; +import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode; @@ -67,12 +68,17 @@ import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryState; import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode; import org.eclipse.jgit.merge.MergeStrategy; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.transport.FetchResult; import org.eclipse.jgit.transport.TagOpt; @@ -339,6 +345,45 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> { PullResult result; if (pullRebaseMode != BranchRebaseMode.NONE) { + try { + Ref head = repo.exactRef(Constants.HEAD); + if (head == null) { + throw new NoHeadException(JGitText + .get().commitOnRepoWithoutHEADCurrentlyNotSupported); + } + ObjectId headId = head.getObjectId(); + if (headId == null) { + // Pull on an unborn branch: checkout + try (RevWalk revWalk = new RevWalk(repo)) { + RevCommit srcCommit = revWalk + .parseCommit(commitToMerge); + DirCacheCheckout dco = new DirCacheCheckout(repo, + repo.lockDirCache(), srcCommit.getTree()); + dco.setFailOnConflict(true); + dco.setProgressMonitor(monitor); + dco.checkout(); + RefUpdate refUpdate = repo + .updateRef(head.getTarget().getName()); + refUpdate.setNewObjectId(commitToMerge); + refUpdate.setExpectedOldObjectId(null); + refUpdate.setRefLogMessage("initial pull", false); //$NON-NLS-1$ + if (refUpdate.update() != Result.NEW) { + throw new NoHeadException(JGitText + .get().commitOnRepoWithoutHEADCurrentlyNotSupported); + } + monitor.endTask(); + return new PullResult(fetchRes, remote, + RebaseResult.result( + RebaseResult.Status.FAST_FORWARD, + srcCommit)); + } + } + } catch (NoHeadException e) { + throw e; + } catch (IOException e) { + throw new JGitInternalException(JGitText + .get().exceptionCaughtDuringExecutionOfPullCommand, e); + } RebaseCommand rebase = new RebaseCommand(repo); RebaseResult rebaseRes = rebase.setUpstream(commitToMerge) .setUpstreamName(upstreamName).setProgressMonitor(monitor) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java index 0e3d000d3a..0dacd4dfbf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -362,7 +362,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { List<RebaseTodoLine> steps = repo.readRebaseTodo( rebaseState.getPath(GIT_REBASE_TODO), false); - if (steps.size() == 0) { + if (steps.isEmpty()) { return finishRebase(walk.parseCommit(repo.resolve(Constants.HEAD)), false); } if (isInteractive()) { @@ -490,7 +490,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { resetSoftToParent(); List<RebaseTodoLine> steps = repo.readRebaseTodo( rebaseState.getPath(GIT_REBASE_TODO), false); - RebaseTodoLine nextStep = steps.size() > 0 ? steps.get(0) : null; + RebaseTodoLine nextStep = steps.isEmpty() ? null : steps.get(0); File messageFixupFile = rebaseState.getFile(MESSAGE_FIXUP); File messageSquashFile = rebaseState.getFile(MESSAGE_SQUASH); if (isSquash && messageFixupFile.exists()) @@ -575,7 +575,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { ObjectId headId = getHead().getObjectId(); // getHead() checks for null assert headId != null; - if (!AnyObjectId.equals(headId, newParents.get(0))) + if (!AnyObjectId.isEqual(headId, newParents.get(0))) checkoutCommit(headId.getName(), newParents.get(0)); // Use the cherry-pick strategy if all non-first parents did not @@ -1083,7 +1083,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { repo.writeRebaseTodoFile(rebaseState.getPath(GIT_REBASE_TODO), todoLines, false); - if (poppedLines.size() > 0) { + if (!poppedLines.isEmpty()) { repo.writeRebaseTodoFile(rebaseState.getPath(DONE), poppedLines, true); } @@ -1295,13 +1295,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> { } } return newCommit; - } catch (RefAlreadyExistsException e) { - throw new JGitInternalException(e.getMessage(), e); - } catch (RefNotFoundException e) { - throw new JGitInternalException(e.getMessage(), e); - } catch (InvalidRefNameException e) { - throw new JGitInternalException(e.getMessage(), e); - } catch (CheckoutConflictException e) { + } catch (RefAlreadyExistsException | RefNotFoundException + | InvalidRefNameException | CheckoutConflictException e) { throw new JGitInternalException(e.getMessage(), e); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java index 46e0df7263..ddd60b6fa2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java @@ -175,8 +175,8 @@ public class RevertCommand extends GitCommand<RevCommit> { + "This reverts commit " + srcCommit.getId().getName() //$NON-NLS-1$ + ".\n"; //$NON-NLS-1$ if (merger.merge(headCommit, srcParent)) { - if (AnyObjectId.equals(headCommit.getTree().getId(), merger - .getResultTreeId())) + if (AnyObjectId.isEqual(headCommit.getTree().getId(), + merger.getResultTreeId())) continue; DirCacheCheckout dco = new DirCacheCheckout(repo, headCommit.getTree(), repo.lockDirCache(), diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java index f92455a96a..8908277725 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java @@ -238,9 +238,7 @@ public class SubmoduleAddCommand extends modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, name, ConfigConstants.CONFIG_KEY_URL, uri); modulesConfig.save(); - } catch (IOException e) { - throw new JGitInternalException(e.getMessage(), e); - } catch (ConfigInvalidException e) { + } catch (IOException | ConfigInvalidException e) { throw new JGitInternalException(e.getMessage(), e); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java index 5a0528b0f5..ebcea4b1dd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java @@ -173,8 +173,8 @@ public class SubmoduleDeinitCommand } final File[] ls = dir.listFiles(); if (ls != null) { - for (int i = 0; i < ls.length; i++) { - FileUtils.delete(ls[i], RECURSIVE); + for (File f : ls) { + FileUtils.delete(f, RECURSIVE); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java index 2db12b8e25..6fd94052a4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java @@ -128,9 +128,7 @@ public class SubmoduleInitCommand extends GitCommand<Collection<String>> { if (!initialized.isEmpty()) config.save(); return initialized; - } catch (IOException e) { - throw new JGitInternalException(e.getMessage(), e); - } catch (ConfigInvalidException e) { + } catch (IOException | ConfigInvalidException e) { throw new JGitInternalException(e.getMessage(), e); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java index 0606c5b8d4..58e59598ed 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java @@ -108,9 +108,7 @@ public class SubmoduleStatusCommand extends statuses.put(status.getPath(), status); } return statuses; - } catch (IOException e) { - throw new JGitInternalException(e.getMessage(), e); - } catch (ConfigInvalidException e) { + } catch (IOException | ConfigInvalidException e) { throw new JGitInternalException(e.getMessage(), e); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java index 7cf4b73af0..52393695d9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java @@ -162,9 +162,7 @@ public class SubmoduleSyncCommand extends GitCommand<Map<String, String>> { if (!synced.isEmpty()) config.save(); return synced; - } catch (IOException e) { - throw new JGitInternalException(e.getMessage(), e); - } catch (ConfigInvalidException e) { + } catch (IOException | ConfigInvalidException e) { throw new JGitInternalException(e.getMessage(), e); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java index e7ad0bc40d..1cecff6fb0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java @@ -1063,7 +1063,7 @@ public class DiffFormatter implements AutoCloseable { entry.newId = id; break; } - } else if (ids.size() == 0) + } else if (ids.isEmpty()) throw new MissingObjectException(id, Constants.OBJ_BLOB); else throw new AmbiguousObjectException(id, ids); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java index a778de910b..f07c24efc4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java @@ -112,14 +112,12 @@ public class DirCache { private static final byte[] NO_CHECKSUM = {}; - static final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() { - @Override - public int compare(DirCacheEntry o1, DirCacheEntry o2) { - final int cr = cmp(o1, o2); - if (cr != 0) - return cr; - return o1.getStage() - o2.getStage(); - } + static final Comparator<DirCacheEntry> ENT_CMP = (DirCacheEntry o1, + DirCacheEntry o2) -> { + final int cr = cmp(o1, o2); + if (cr != 0) + return cr; + return o1.getStage() - o2.getStage(); }; static int cmp(DirCacheEntry a, DirCacheEntry b) { @@ -254,13 +252,7 @@ public class DirCache { try { c.read(); - } catch (IOException e) { - c.unlock(); - throw e; - } catch (RuntimeException e) { - c.unlock(); - throw e; - } catch (Error e) { + } catch (IOException | RuntimeException | Error e) { c.unlock(); throw e; } @@ -637,13 +629,7 @@ public class DirCache { try (OutputStream o = tmp.getOutputStream(); OutputStream bo = new BufferedOutputStream(o)) { writeTo(liveFile.getParentFile(), bo); - } catch (IOException err) { - tmp.unlock(); - throw err; - } catch (RuntimeException err) { - tmp.unlock(); - throw err; - } catch (Error err) { + } catch (IOException | RuntimeException | Error err) { tmp.unlock(); throw err; } 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 2d6228657a..22090f5b60 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -635,7 +635,7 @@ public class DirCacheCheckout { if (!builder.commit()) throw new IndexWriteException(); } - return toBeDeleted.size() == 0; + return toBeDeleted.isEmpty(); } private void checkoutGitlink(String path, DirCacheEntry entry) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java index 74ba97fb8d..5b8e11f330 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java @@ -75,13 +75,11 @@ import org.eclipse.jgit.util.Paths; * @see DirCacheBuilder */ public class DirCacheEditor extends BaseDirCacheEditor { - private static final Comparator<PathEdit> EDIT_CMP = new Comparator<PathEdit>() { - @Override - public int compare(PathEdit o1, PathEdit o2) { - final byte[] a = o1.path; - final byte[] b = o2.path; - return cmp(a, a.length, b, b.length); - } + private static final Comparator<PathEdit> EDIT_CMP = (PathEdit o1, + PathEdit o2) -> { + final byte[] a = o1.path; + final byte[] b = o2.path; + return cmp(a, a.length, b, b.length); }; private final List<PathEdit> edits; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java index b118fd6bc9..0e91f0d748 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java @@ -448,9 +448,9 @@ public class DirCacheEntry { */ public void setAssumeValid(boolean assume) { if (assume) - info[infoOffset + P_FLAGS] |= ASSUME_VALID; + info[infoOffset + P_FLAGS] |= (byte) ASSUME_VALID; else - info[infoOffset + P_FLAGS] &= ~ASSUME_VALID; + info[infoOffset + P_FLAGS] &= (byte) ~ASSUME_VALID; } /** @@ -470,9 +470,9 @@ public class DirCacheEntry { */ public void setUpdateNeeded(boolean updateNeeded) { if (updateNeeded) - inCoreFlags |= UPDATE_NEEDED; + inCoreFlags |= (byte) UPDATE_NEEDED; else - inCoreFlags &= ~UPDATE_NEEDED; + inCoreFlags &= (byte) ~UPDATE_NEEDED; } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java index 11a3474a35..80e1084fe0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java @@ -81,25 +81,26 @@ public class DirCacheTree { private static final DirCacheTree[] NO_CHILDREN = {}; - private static final Comparator<DirCacheTree> TREE_CMP = new Comparator<DirCacheTree>() { - @Override - public int compare(DirCacheTree o1, DirCacheTree o2) { - final byte[] a = o1.encodedName; - final byte[] b = o2.encodedName; - final int aLen = a.length; - final int bLen = b.length; - int cPos; - for (cPos = 0; cPos < aLen && cPos < bLen; cPos++) { - final int cmp = (a[cPos] & 0xff) - (b[cPos] & 0xff); - if (cmp != 0) - return cmp; + private static final Comparator<DirCacheTree> TREE_CMP = (DirCacheTree o1, + DirCacheTree o2) -> { + final byte[] a = o1.encodedName; + final byte[] b = o2.encodedName; + final int aLen = a.length; + final int bLen = b.length; + int cPos; + for (cPos = 0; cPos < aLen && cPos < bLen; cPos++) { + final int cmp = (a[cPos] & 0xff) - (b[cPos] & 0xff); + if (cmp != 0) { + return cmp; } - if (aLen == bLen) - return 0; - if (aLen < bLen) - return '/' - (b[cPos] & 0xff); - return (a[cPos] & 0xff) - '/'; } + if (aLen == bLen) { + return 0; + } + if (aLen < bLen) { + return '/' - (b[cPos] & 0xff); + } + return (a[cPos] & 0xff) - '/'; }; /** Tree this tree resides in; null if we are the root. */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/IncorrectObjectTypeException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/IncorrectObjectTypeException.java index 5abd0c347b..15fd803d00 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/IncorrectObjectTypeException.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/IncorrectObjectTypeException.java @@ -63,7 +63,7 @@ public class IncorrectObjectTypeException extends IOException { private static final long serialVersionUID = 1L; /** - * Construct and IncorrectObjectTypeException for the specified object id. + * Construct an IncorrectObjectTypeException for the specified object id. * * Provide the type to make it easier to track down the problem. * @@ -75,7 +75,7 @@ public class IncorrectObjectTypeException extends IOException { } /** - * Construct and IncorrectObjectTypeException for the specified object id. + * Construct an IncorrectObjectTypeException for the specified object id. * * Provide the type to make it easier to track down the problem. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java index 2ad87dade6..239e2c81d3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java @@ -64,7 +64,7 @@ public final class WildMatcher extends AbstractMatcher { public final boolean matches(String path, boolean assumeDirectory, boolean pathMatch) { return !dirOnly || assumeDirectory - || !pathMatch && isSubdirectory(path); + || (!pathMatch && isSubdirectory(path)); } /** {@inheritDoc} */ 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 d51e052b03..60d1dab23f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -191,6 +191,7 @@ public class JGitText extends TranslationBundle { /***/ public String configSubsectionContainsNullByte; /***/ public String configValueContainsNullByte; /***/ public String configHandleIsStale; + /***/ public String configHandleMayBeLocked; /***/ public String connectionFailed; /***/ public String connectionTimeOut; /***/ public String contextMustBeNonNegative; @@ -245,9 +246,14 @@ public class JGitText extends TranslationBundle { /***/ public String corruptObjectZeroId; /***/ public String corruptPack; /***/ public String corruptUseCnt; + /***/ public String couldNotFindTabInLine; + /***/ public String couldNotFindSixTabsInLine; /***/ public String couldNotGetAdvertisedRef; /***/ public String couldNotGetRepoStatistics; /***/ public String couldNotLockHEAD; + /***/ public String couldNotPersistCookies; + /***/ public String couldNotReadCookieFile; + /***/ public String couldNotReadIndexInOneGo; /***/ public String couldNotReadObjectWhileParsingCommit; /***/ public String couldNotRewindToUpstreamCommit; /***/ public String couldNotURLEncodeToUTF8; @@ -357,6 +363,7 @@ public class JGitText extends TranslationBundle { /***/ public String gpgNoKeyInLegacySecring; /***/ public String gpgNoPublicKeyFound; /***/ public String gpgNoSecretKeyForPublicKey; + /***/ public String gpgNotASigningKey; /***/ public String gpgKeyInfo; /***/ public String gpgSigningCancelled; /***/ public String headRequiredToStash; @@ -472,6 +479,7 @@ public class JGitText extends TranslationBundle { /***/ public String mismatchCRC; /***/ public String missingAccesskey; /***/ public String missingConfigurationForKey; + /***/ public String missingCookieFile; /***/ public String missingCRC; /***/ public String missingDeltaBase; /***/ public String missingForwardImageInGITBinaryPatch; @@ -637,6 +645,7 @@ public class JGitText extends TranslationBundle { /***/ public String s3ActionReading; /***/ public String s3ActionWriting; /***/ public String saveFileStoreAttributesFailed; + /***/ public String searchForReachableBranches; /***/ public String searchForReuse; /***/ public String searchForSizes; /***/ public String secondsAgo; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java index fa32b267df..c0364acdd1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java @@ -385,12 +385,7 @@ public abstract class KetchLeader { private void scheduleLeader() { idle = false; - system.getExecutor().execute(new Runnable() { - @Override - public void run() { - runLeader(); - } - }); + system.getExecutor().execute(this::runLeader); } private void runLeader() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java index a0176d7d2e..0e8377dd02 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java @@ -345,7 +345,7 @@ public abstract class KetchReplica { } private static boolean equals(@Nullable ObjectId a, LogIndex b) { - return a != null && b != null && AnyObjectId.equals(a, b); + return a != null && b != null && AnyObjectId.isEqual(a, b); } /** @@ -749,7 +749,7 @@ public abstract class KetchReplica { Ref oldRef = remote.remove(name); ObjectId oldId = getId(oldRef); ObjectId newId = tw.getObjectId(0); - if (!AnyObjectId.equals(oldId, newId)) { + if (!AnyObjectId.isEqual(oldId, newId)) { delta.add(new ReceiveCommand(oldId, newId, name)); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java index c09d872f0a..53fd198006 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java @@ -106,7 +106,7 @@ class LagCheck implements AutoCloseable { return UNKNOWN; } - if (AnyObjectId.equals(remoteId, ObjectId.zeroId())) { + if (AnyObjectId.isEqual(remoteId, ObjectId.zeroId())) { // Replica does not have the txnAccepted reference. return LAGGING; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java index 6f1f5c5c2c..cd0ded5e38 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java @@ -123,21 +123,18 @@ public class LocalReplica extends KetchReplica { /** {@inheritDoc} */ @Override protected void startPush(ReplicaPushRequest req) { - getSystem().getExecutor().execute(new Runnable() { - @Override - public void run() { - MonotonicClock clk = getSystem().getClock(); - try (Repository git = getLeader().openRepository(); - ProposedTimestamp ts = clk.propose()) { - try { - update(git, req, ts); - req.done(git); - } catch (Throwable err) { - req.setException(git, err); - } - } catch (IOException err) { - req.setException(null, err); + getSystem().getExecutor().execute(() -> { + MonotonicClock clk = getSystem().getClock(); + try (Repository git = getLeader().openRepository(); + ProposedTimestamp ts = clk.propose()) { + try { + update(git, req, ts); + req.done(git); + } catch (Throwable err) { + req.setException(git, err); } + } catch (IOException err) { + req.setException(null, err); } }); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java index b61274ef4a..4bed575f26 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java @@ -139,19 +139,16 @@ public class RemoteGitReplica extends KetchReplica { /** {@inheritDoc} */ @Override protected void startPush(ReplicaPushRequest req) { - getSystem().getExecutor().execute(new Runnable() { - @Override - public void run() { - try (Repository git = getLeader().openRepository()) { - try { - push(git, req); - req.done(git); - } catch (Throwable err) { - req.setException(git, err); - } - } catch (IOException err) { - req.setException(null, err); + getSystem().getExecutor().execute(() -> { + try (Repository git = getLeader().openRepository()) { + try { + push(git, req); + req.done(git); + } catch (Throwable err) { + req.setException(git, err); } + } catch (IOException err) { + req.setException(null, err); } }); } @@ -203,7 +200,7 @@ public class RemoteGitReplica extends KetchReplica { private static boolean isExpectedValue(Map<String, Ref> adv, RemoteRefUpdate u) { Ref r = adv.get(u.getRemoteName()); - if (!AnyObjectId.equals(getId(r), u.getExpectedOldObjectId())) { + if (!AnyObjectId.isEqual(getId(r), u.getExpectedOldObjectId())) { ((RemoteCommand) u).cmd.setResult(LOCK_FAILURE); return false; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/StageBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/StageBuilder.java index ae82dced21..815984deb2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/StageBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/StageBuilder.java @@ -138,7 +138,7 @@ public class StageBuilder { try (RevWalk rw = new RevWalk(git); TreeWalk tw = new TreeWalk(rw.getObjectReader()); ObjectInserter ins = git.newObjectInserter()) { - if (AnyObjectId.equals(oldTree, ObjectId.zeroId())) { + if (AnyObjectId.isEqual(oldTree, ObjectId.zeroId())) { tw.addTree(new EmptyTreeIterator()); } else { tw.addTree(rw.parseTree(oldTree)); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java index bd4b4d23f4..3b92deeaeb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java @@ -180,8 +180,8 @@ final class DeltaBaseCache { int getMemoryUsedByTableForTest() { int r = 0; - for (int i = 0; i < table.length; i++) { - for (Entry e = table[i]; e != null; e = e.tableNext) { + for (Entry t : table) { + for (Entry e = t; e != null; e = e.tableNext) { r += e.data.length; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java index ca11fb9265..f10a1d8127 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java @@ -661,7 +661,7 @@ public class DfsGarbageCollector { private int objectsBefore() { int cnt = 0; for (DfsPackFile p : packsBefore) - cnt += p.getPackDescription().getObjectCount(); + cnt += (int) p.getPackDescription().getObjectCount(); return cnt; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java index 127ee6bf11..6f3f2bd8e7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java @@ -469,12 +469,8 @@ public class DfsPackCompactor { continue SCAN; want.add(new ObjectIdWithOffset(id, ent.getOffset())); } - Collections.sort(want, new Comparator<ObjectIdWithOffset>() { - @Override - public int compare(ObjectIdWithOffset a, ObjectIdWithOffset b) { - return Long.signum(a.offset - b.offset); - } - }); + Collections.sort(want, (ObjectIdWithOffset a, + ObjectIdWithOffset b) -> Long.signum(a.offset - b.offset)); return want; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackParser.java index 3a30d7daf5..e4c37cb4c2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackParser.java @@ -429,10 +429,10 @@ public class DfsPackParser extends PackParser { final byte[] buf = buffer(); int sz = data.length; int len = 0; - buf[len++] = (byte) ((typeCode << 4) | sz & 15); + buf[len++] = (byte) ((typeCode << 4) | (sz & 15)); sz >>>= 4; while (sz > 0) { - buf[len - 1] |= 0x80; + buf[len - 1] |= (byte) 0x80; buf[len++] = (byte) (sz & 0x7f); sz >>>= 7; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java index d04709f6c2..c75b88f3bd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java @@ -287,14 +287,12 @@ public class DfsReader extends ObjectReader implements ObjectReuseAsIs { return Collections.emptySet(); } - private static final Comparator<FoundObject<?>> FOUND_OBJECT_SORT = new Comparator<FoundObject<?>>() { - @Override - public int compare(FoundObject<?> a, FoundObject<?> b) { - int cmp = a.packIndex - b.packIndex; - if (cmp == 0) - cmp = Long.signum(a.offset - b.offset); - return cmp; - } + private static final Comparator<FoundObject<?>> FOUND_OBJECT_SORT = ( + FoundObject<?> a, FoundObject<?> b) -> { + int cmp = a.packIndex - b.packIndex; + if (cmp == 0) + cmp = Long.signum(a.offset - b.offset); + return cmp; }; private static class FoundObject<T extends ObjectId> { @@ -565,12 +563,9 @@ public class DfsReader extends ObjectReader implements ObjectReuseAsIs { return new DfsObjectToPack(objectId, type); } - private static final Comparator<DfsObjectToPack> OFFSET_SORT = new Comparator<DfsObjectToPack>() { - @Override - public int compare(DfsObjectToPack a, DfsObjectToPack b) { - return Long.signum(a.getOffset() - b.getOffset()); - } - }; + private static final Comparator<DfsObjectToPack> OFFSET_SORT = ( + DfsObjectToPack a, + DfsObjectToPack b) -> Long.signum(a.getOffset() - b.getOffset()); @Override public void selectObjectRepresentation(PackWriter packer, @@ -757,6 +752,7 @@ public class DfsReader extends ObjectReader implements ObjectReuseAsIs { */ int inflate(DfsPackFile pack, long position, byte[] dstbuf, boolean headerOnly) throws IOException, DataFormatException { + long start = System.nanoTime(); prepareInflater(); pin(pack, position); position += block.setInput(position, inf); @@ -765,6 +761,7 @@ public class DfsReader extends ObjectReader implements ObjectReuseAsIs { dstoff += n; if (inf.finished() || (headerOnly && dstoff == dstbuf.length)) { stats.inflatedBytes += dstoff; + stats.inflationMicros += BlockBasedFile.elapsedMicros(start); return dstoff; } else if (inf.needsInput()) { pin(pack, position); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java index c35801f3b0..d6401a1640 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java @@ -85,6 +85,9 @@ public class DfsReaderIoStats { /** Total number of bytes decompressed. */ long inflatedBytes; + /** Total microseconds spent inflating compressed bytes. */ + long inflationMicros; + Accumulator() { } } @@ -186,4 +189,13 @@ public class DfsReaderIoStats { public long getInflatedBytes() { return stats.inflatedBytes; } + + /** + * Get total microseconds spent inflating compressed bytes. + * + * @return total microseconds inflating compressed bytes. + */ + public long getInflationMicros() { + return stats.inflationMicros; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java index 8b2a03d4c5..732cd4d1c6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.internal.storage.dfs; +import static org.eclipse.jgit.lib.Ref.UNDEFINED_UPDATE_INDEX; import static org.eclipse.jgit.lib.Ref.Storage.NEW; import java.io.IOException; @@ -175,7 +176,7 @@ public abstract class DfsRefDatabase extends RefDatabase { cachePeeledState(oldLeaf, newLeaf); } - return recreate(ref, newLeaf); + return recreate(ref, newLeaf, hasVersioning()); } Ref doPeel(Ref leaf) throws MissingObjectException, @@ -187,20 +188,26 @@ public abstract class DfsRefDatabase extends RefDatabase { leaf.getStorage(), leaf.getName(), leaf.getObjectId(), - rw.peel(obj).copy()); + rw.peel(obj).copy(), + hasVersioning() ? leaf.getUpdateIndex() + : UNDEFINED_UPDATE_INDEX); } else { return new ObjectIdRef.PeeledNonTag( leaf.getStorage(), leaf.getName(), - leaf.getObjectId()); + leaf.getObjectId(), + hasVersioning() ? leaf.getUpdateIndex() + : UNDEFINED_UPDATE_INDEX); } } } - static Ref recreate(Ref old, Ref leaf) { + static Ref recreate(Ref old, Ref leaf, boolean hasVersioning) { if (old.isSymbolic()) { - Ref dst = recreate(old.getTarget(), leaf); - return new SymbolicRef(old.getName(), dst); + Ref dst = recreate(old.getTarget(), leaf, hasVersioning); + return new SymbolicRef(old.getName(), dst, + hasVersioning ? old.getUpdateIndex() + : UNDEFINED_UPDATE_INDEX); } return leaf; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java index 83394bb92c..6050c15992 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java @@ -47,8 +47,10 @@ import java.io.IOException; import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import org.eclipse.jgit.annotations.Nullable; @@ -277,12 +279,31 @@ public class DfsReftableDatabase extends DfsRefDatabase { /** {@inheritDoc} */ @Override + public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException { + if (!getReftableConfig().isIndexObjects()) { + return super.getTipsWithSha1(id); + } + lock.lock(); + try { + RefCursor cursor = reader().byObjectId(id); + Set<Ref> refs = new HashSet<>(); + while (cursor.next()) { + refs.add(cursor.getRef()); + } + return refs; + } finally { + lock.unlock(); + } + } + + /** {@inheritDoc} */ + @Override public Ref peel(Ref ref) throws IOException { Ref oldLeaf = ref.getLeaf(); if (oldLeaf.isPeeled() || oldLeaf.getObjectId() == null) { return ref; } - return recreate(ref, doPeel(oldLeaf)); + return recreate(ref, doPeel(oldLeaf), hasVersioning()); } @Override @@ -315,6 +336,7 @@ public class DfsReftableDatabase extends DfsRefDatabase { throws IOException { ReceiveCommand cmd = toCommand(oldRef, newRef); try (RevWalk rw = new RevWalk(getRepository())) { + rw.setRetainBody(false); newBatchUpdate().setAllowNonFastForwards(true).addCommand(cmd) .execute(rw, NullProgressMonitor.INSTANCE); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java index 5169e929e4..8e5c5a7f75 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java @@ -126,6 +126,12 @@ public abstract class DfsRepository extends Repository { /** {@inheritDoc} */ @Override + public String getIdentifier() { + return getDescription().getRepositoryName(); + } + + /** {@inheritDoc} */ + @Override public void scanForRepoChanges() throws IOException { getRefDatabase().refresh(); getObjectDatabase().clearCache(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReftableBatchRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReftableBatchRefUpdate.java index 47ac4ec72e..07fd00f149 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReftableBatchRefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReftableBatchRefUpdate.java @@ -252,7 +252,7 @@ public class ReftableBatchRefUpdate extends BatchRefUpdate { private static boolean matchOld(ReceiveCommand cmd, @Nullable Ref ref) { if (ref == null) { - return AnyObjectId.equals(ObjectId.zeroId(), cmd.getOldId()) + return AnyObjectId.isEqual(ObjectId.zeroId(), cmd.getOldId()) && cmd.getOldSymref() == null; } else if (ref.isSymbolic()) { return ref.getTarget().getName().equals(cmd.getOldSymref()); @@ -368,7 +368,7 @@ public class ReftableBatchRefUpdate extends BatchRefUpdate { String name = cmd.getRefName(); ObjectId newId = cmd.getNewId(); String newSymref = cmd.getNewSymref(); - if (AnyObjectId.equals(ObjectId.zeroId(), newId) + if (AnyObjectId.isEqual(ObjectId.zeroId(), newId) && newSymref == null) { refs.add(new ObjectIdRef.Unpeeled(NEW, name, null)); continue; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java index 356d64b563..4f5f8a613e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java @@ -62,8 +62,6 @@ import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.attributes.AttributesNode; import org.eclipse.jgit.attributes.AttributesNodeProvider; import org.eclipse.jgit.errors.ConfigInvalidException; -import org.eclipse.jgit.events.ConfigChangedEvent; -import org.eclipse.jgit.events.ConfigChangedListener; import org.eclipse.jgit.events.IndexChangedEvent; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateHandle; @@ -192,12 +190,7 @@ public class FileRepository extends Repository { getFS()); loadRepoConfig(); - repoConfig.addChangeListener(new ConfigChangedListener() { - @Override - public void onConfigChanged(ConfigChangedEvent event) { - fireEvent(event); - } - }); + repoConfig.addChangeListener(this::fireEvent); final long repositoryFormatVersion = getConfig().getLong( ConfigConstants.CONFIG_CORE_SECTION, null, @@ -361,6 +354,17 @@ public class FileRepository extends Repository { /** {@inheritDoc} */ @Override + public String getIdentifier() { + File directory = getDirectory(); + if (directory != null) { + return directory.getPath(); + } else { + throw new IllegalStateException(); + } + } + + /** {@inheritDoc} */ + @Override public FileBasedConfig getConfig() { try { SystemReader.getInstance().getUserConfig(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java index 976f946e5d..8650ebfe29 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java @@ -413,6 +413,7 @@ public class FileSnapshot { * the other snapshot. * @return true if the two snapshots share the same information. */ + @SuppressWarnings("NonOverridingEquals") public boolean equals(FileSnapshot other) { boolean sizeEq = size == UNKNOWN_SIZE || other.size == UNKNOWN_SIZE || size == other.size; return lastModified.equals(other.lastModified) && sizeEq @@ -490,7 +491,7 @@ public class FileSnapshot { } /** {@inheritDoc} */ - @SuppressWarnings("nls") + @SuppressWarnings({ "nls", "ReferenceEquality" }) @Override public String toString() { if (this == DIRTY) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java index 7400308c86..08bb6cb7fa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java @@ -244,7 +244,7 @@ public class GC { * If the configuration parameter "gc.pruneexpire" couldn't be * parsed */ - // TODO(ms): in 5.0 change signature and return Future<Collection<PackFile>> + // TODO(ms): change signature and return Future<Collection<PackFile>> @SuppressWarnings("FutureReturnValueIgnored") public Collection<PackFile> gc() throws IOException, ParseException { if (!background) { @@ -281,7 +281,7 @@ public class GC { } return Collections.emptyList(); }; - // TODO(ms): in 5.0 change signature and return the Future + // TODO(ms): change signature and return the Future executor().submit(gcTask); return Collections.emptyList(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java index f7e78b94f7..6af41256d6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java @@ -120,12 +120,8 @@ public class LockFile { } /** Filter to skip over active lock files when listing a directory. */ - static final FilenameFilter FILTER = new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return !name.endsWith(LOCK_SUFFIX); - } - }; + static final FilenameFilter FILTER = (File dir, + String name) -> !name.endsWith(LOCK_SUFFIX); private final File ref; @@ -248,13 +244,7 @@ public class LockFile { // Don't worry about a file that doesn't exist yet, it // conceptually has no current content to copy. // - } catch (IOException ioe) { - unlock(); - throw ioe; - } catch (RuntimeException ioe) { - unlock(); - throw ioe; - } catch (Error ioe) { + } catch (IOException | RuntimeException | Error ioe) { unlock(); throw ioe; } @@ -308,13 +298,7 @@ public class LockFile { } os.close(); os = null; - } catch (IOException ioe) { - unlock(); - throw ioe; - } catch (RuntimeException ioe) { - unlock(); - throw ioe; - } catch (Error ioe) { + } catch (IOException | RuntimeException | Error ioe) { unlock(); throw ioe; } @@ -362,13 +346,7 @@ public class LockFile { os.getChannel().force(true); out.close(); os = null; - } catch (IOException ioe) { - unlock(); - throw ioe; - } catch (RuntimeException ioe) { - unlock(); - throw ioe; - } catch (Error ioe) { + } catch (IOException | RuntimeException | Error ioe) { unlock(); throw ioe; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java index 6e8a15e86d..7d31673566 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java @@ -353,10 +353,10 @@ public class ObjectDirectoryPackParser extends PackParser { final byte[] buf = buffer(); int sz = data.length; int len = 0; - buf[len++] = (byte) ((typeCode << 4) | sz & 15); + buf[len++] = (byte) ((typeCode << 4) | (sz & 15)); sz >>>= 4; while (sz > 0) { - buf[len - 1] |= 0x80; + buf[len - 1] |= (byte) 0x80; buf[len++] = (byte) (sz & 0x7f); sz >>>= 7; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java index eff7958748..9941ff3740 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java @@ -45,7 +45,6 @@ package org.eclipse.jgit.internal.storage.file; import java.text.MessageFormat; import java.util.Collections; -import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; @@ -133,12 +132,8 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex { for (int i = 0; i < entries.size(); i++) { positionEntries.add(new PositionEntry(entries.get(i), i)); } - Collections.sort(entries, new Comparator<ObjectToPack>() { - @Override - public int compare(ObjectToPack a, ObjectToPack b) { - return Long.signum(a.getOffset() - b.getOffset()); - } - }); + Collections.sort(entries, (ObjectToPack a, ObjectToPack b) -> Long + .signum(a.getOffset() - b.getOffset())); for (int i = 0; i < entries.size(); i++) { PositionEntry e = positionEntries.get(entries.get(i)); e.offsetPosition = i; @@ -310,57 +305,55 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex { public Iterable<StoredEntry> getCompressedBitmaps() { // Add order is from oldest to newest. The reverse add order is the // output order. - return new Iterable<StoredEntry>() { + return () -> new Iterator<StoredEntry>() { + + private int index = byAddOrder.size() - 1; + @Override - public Iterator<StoredEntry> iterator() { - return new Iterator<StoredEntry>() { - private int index = byAddOrder.size() - 1; + public boolean hasNext() { + return index >= 0; + } - @Override - public boolean hasNext() { - return index >= 0; + @Override + public StoredEntry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + StoredBitmap item = byAddOrder.get(index); + int bestXorOffset = 0; + EWAHCompressedBitmap bestBitmap = item.getBitmap(); + + // Attempt to compress the bitmap with an XOR of the + // previously written entries. + for (int i = 1; i <= MAX_XOR_OFFSET_SEARCH; i++) { + int curr = i + index; + if (curr >= byAddOrder.size()) { + break; } - @Override - public StoredEntry next() { - if (!hasNext()) - throw new NoSuchElementException(); - StoredBitmap item = byAddOrder.get(index); - int bestXorOffset = 0; - EWAHCompressedBitmap bestBitmap = item.getBitmap(); - - // Attempt to compress the bitmap with an XOR of the - // previously written entries. - for (int i = 1; i <= MAX_XOR_OFFSET_SEARCH; i++) { - int curr = i + index; - if (curr >= byAddOrder.size()) - break; - - StoredBitmap other = byAddOrder.get(curr); - EWAHCompressedBitmap bitmap = other.getBitmap() - .xor(item.getBitmap()); - - if (bitmap.sizeInBytes() - < bestBitmap.sizeInBytes()) { - bestBitmap = bitmap; - bestXorOffset = i; - } - } - index--; - - PositionEntry entry = positionEntries.get(item); - if (entry == null) - throw new IllegalStateException(); - bestBitmap.trim(); - return new StoredEntry(entry.namePosition, bestBitmap, - bestXorOffset, item.getFlags()); - } + StoredBitmap other = byAddOrder.get(curr); + EWAHCompressedBitmap bitmap = other.getBitmap() + .xor(item.getBitmap()); - @Override - public void remove() { - throw new UnsupportedOperationException(); + if (bitmap.sizeInBytes() < bestBitmap.sizeInBytes()) { + bestBitmap = bitmap; + bestXorOffset = i; } - }; + } + index--; + + PositionEntry entry = positionEntries.get(item); + if (entry == null) { + throw new IllegalStateException(); + } + bestBitmap.trim(); + return new StoredEntry(entry.namePosition, bestBitmap, + bestXorOffset, item.getFlags()); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); } }; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java index a4729bba48..e5cea6c010 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java @@ -745,7 +745,7 @@ public class RefDirectory extends RefDatabase { for (LockFile ol : heldLocks.values()) { ol.requireLock(); } - if (refs.size() == 0) { + if (refs.isEmpty()) { return null; } FS fs = parent.getFS(); @@ -1128,9 +1128,11 @@ public class RefDirectory extends RefDatabase { // check whether the found new ref is the an additional ref. These refs // should not go into looseRefs - for (int i = 0; i < additionalRefsNames.length; i++) - if (name.equals(additionalRefsNames[i])) + for (String additionalRefsName : additionalRefsNames) { + if (name.equals(additionalRefsName)) { return n; + } + } if (looseRefs.compareAndSet(curList, curList.add(idx, n))) modCnt.incrementAndGet(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java index cf474afbbe..79f1307578 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java @@ -295,7 +295,7 @@ public class UnpackedObject { * can always correctly determine the buffer format. */ final int fb = hdr[0] & 0xff; - return (fb & 0x8f) == 0x08 && (((fb << 8) | hdr[1] & 0xff) % 31) == 0; + return (fb & 0x8f) == 0x08 && (((fb << 8) | (hdr[1] & 0xff)) % 31) == 0; } static InputStream inflate(final InputStream in, final long size, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java index 967754a627..ea0d269053 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java @@ -112,7 +112,7 @@ class UnpackedObjectCache { if (obj == null) break; - if (AnyObjectId.equals(obj, toFind)) + if (AnyObjectId.isEqual(obj, toFind)) return true; if (++i == ids.length()) @@ -132,7 +132,7 @@ class UnpackedObjectCache { continue; } - if (AnyObjectId.equals(obj, toAdd)) + if (AnyObjectId.isEqual(obj, toAdd)) return true; if (++i == ids.length()) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BinaryDelta.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BinaryDelta.java index c7e5ad6232..5f69d0a884 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BinaryDelta.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BinaryDelta.java @@ -142,7 +142,7 @@ public class BinaryDelta { int c, shift = 0; do { c = delta[deltaPtr++] & 0xff; - baseLen |= ((long) (c & 0x7f)) << shift; + baseLen |= (c & 0x7f) << shift; shift += 7; } while ((c & 0x80) != 0); if (base.length != baseLen) @@ -155,7 +155,7 @@ public class BinaryDelta { shift = 0; do { c = delta[deltaPtr++] & 0xff; - resLen |= ((long) (c & 0x7f)) << shift; + resLen |= (c & 0x7f) << shift; shift += 7; } while ((c & 0x80) != 0); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java index 0347644324..a211d164c4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java @@ -46,7 +46,6 @@ package org.eclipse.jgit.internal.storage.pack; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -212,12 +211,8 @@ final class DeltaTask implements Callable<Object> { } // Sort by starting index to identify gaps later. - Collections.sort(topPaths, new Comparator<WeightedPath>() { - @Override - public int compare(WeightedPath a, WeightedPath b) { - return a.slice.beginIndex - b.slice.beginIndex; - } - }); + Collections.sort(topPaths, (WeightedPath a, + WeightedPath b) -> a.slice.beginIndex - b.slice.beginIndex); bytesPerUnit = 1; while (MAX_METER <= (totalWeight / bytesPerUnit)) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java index a047534fbf..d152a392f0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java @@ -365,9 +365,7 @@ final class DeltaWindow { resObj.setCachedDelta(deltaCache.cache(zbuf, len, deltaLen)); resObj.setCachedSize(deltaLen); - } catch (IOException err) { - deltaCache.credit(deltaLen); - } catch (OutOfMemoryError err) { + } catch (IOException | OutOfMemoryError err) { deltaCache.credit(deltaLen); } } 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 1e3d74ab57..6506789218 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 @@ -44,6 +44,7 @@ package org.eclipse.jgit.internal.storage.pack; +import static java.util.Objects.requireNonNull; import static org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation.PACK_DELTA; import static org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation.PACK_WHOLE; import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH; @@ -61,7 +62,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -119,6 +120,7 @@ import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.revwalk.RevTree; 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.WriteAbortedException; import org.eclipse.jgit.util.BlockList; @@ -303,7 +305,7 @@ public class PackWriter implements AutoCloseable { private ObjectCountCallback callback; - private long filterBlobLimit = -1; + private FilterSpec filterSpec = FilterSpec.NO_FILTER; /** * Create writer for specified repository. @@ -641,10 +643,11 @@ public class PackWriter implements AutoCloseable { } /** - * @param bytes exclude blobs of size greater than this + * @param filter the filter which indicates what and what not this writer + * should include */ - public void setFilterBlobLimit(long bytes) { - filterBlobLimit = bytes; + public void setFilterSpec(@NonNull FilterSpec filter) { + filterSpec = requireNonNull(filter); } /** @@ -870,6 +873,37 @@ public class PackWriter implements AutoCloseable { } /** + * A visitation policy which uses the depth at which the object is seen to + * decide if re-traversal is necessary. In particular, if the object has + * already been visited at this depth or shallower, it is not necessary to + * re-visit at this depth. + */ + private static class DepthAwareVisitationPolicy + implements ObjectWalk.VisitationPolicy { + private final Map<ObjectId, Integer> lowestDepthVisited = new HashMap<>(); + + private final ObjectWalk walk; + + DepthAwareVisitationPolicy(ObjectWalk walk) { + this.walk = requireNonNull(walk); + } + + @Override + public boolean shouldVisit(RevObject o) { + Integer lastDepth = lowestDepthVisited.get(o); + if (lastDepth == null) { + return true; + } + return walk.getTreeDepth() < lastDepth.intValue(); + } + + @Override + public void visited(RevObject o) { + lowestDepthVisited.put(o, Integer.valueOf(walk.getTreeDepth())); + } + } + + /** * Prepare the list of objects to be written to the pack stream. * <p> * Basing on these 2 sets, another set of objects to put in a pack file is @@ -910,6 +944,9 @@ public class PackWriter implements AutoCloseable { if (shallowPack && !(walk instanceof DepthWalk.ObjectWalk)) throw new IllegalArgumentException( JGitText.get().shallowPacksRequireDepthWalk); + if (filterSpec.getTreeDepthLimit() >= 0) { + walk.setVisitationPolicy(new DepthAwareVisitationPolicy(walk)); + } findObjectsToPack(countingMonitor, walk, interestingObjects, uninterestingObjects, noBitmaps); } @@ -1380,32 +1417,33 @@ public class PackWriter implements AutoCloseable { // applies "Linus' Law" which states that newer files tend to be the // bigger ones, because source files grow and hardly ever shrink. // - Arrays.sort(list, 0, cnt, new Comparator<ObjectToPack>() { - @Override - public int compare(ObjectToPack a, ObjectToPack b) { - int cmp = (a.isDoNotDelta() ? 1 : 0) - - (b.isDoNotDelta() ? 1 : 0); - if (cmp != 0) - return cmp; - - cmp = a.getType() - b.getType(); - if (cmp != 0) - return cmp; - - cmp = (a.getPathHash() >>> 1) - (b.getPathHash() >>> 1); - if (cmp != 0) - return cmp; - - cmp = (a.getPathHash() & 1) - (b.getPathHash() & 1); - if (cmp != 0) - return cmp; - - cmp = (a.isEdge() ? 0 : 1) - (b.isEdge() ? 0 : 1); - if (cmp != 0) - return cmp; - - return b.getWeight() - a.getWeight(); + Arrays.sort(list, 0, cnt, (ObjectToPack a, ObjectToPack b) -> { + int cmp = (a.isDoNotDelta() ? 1 : 0) - (b.isDoNotDelta() ? 1 : 0); + if (cmp != 0) { + return cmp; + } + + cmp = a.getType() - b.getType(); + if (cmp != 0) { + return cmp; } + + cmp = (a.getPathHash() >>> 1) - (b.getPathHash() >>> 1); + if (cmp != 0) { + return cmp; + } + + cmp = (a.getPathHash() & 1) - (b.getPathHash() & 1); + if (cmp != 0) { + return cmp; + } + + cmp = (a.isEdge() ? 0 : 1) - (b.isEdge() ? 0 : 1); + if (cmp != 0) { + return cmp; + } + + return b.getWeight() - a.getWeight(); }); // Above we stored the objects we cannot delta onto the end. @@ -1499,7 +1537,7 @@ public class PackWriter implements AutoCloseable { Executor executor = config.getExecutor(); final List<Throwable> errors = - Collections.synchronizedList(new ArrayList<Throwable>(threads)); + Collections.synchronizedList(new ArrayList<>(threads)); if (executor instanceof ExecutorService) { // Caller supplied us a service, use it directly. runTasks((ExecutorService) executor, pm, taskBlock, errors); @@ -1526,14 +1564,11 @@ public class PackWriter implements AutoCloseable { // asynchronous execution. Wrap everything and hope it // can schedule these for us. for (DeltaTask task : taskBlock.tasks) { - executor.execute(new Runnable() { - @Override - public void run() { - try { - task.call(); - } catch (Throwable failure) { - errors.add(failure); - } + executor.execute(() -> { + try { + task.call(); + } catch (Throwable failure) { + errors.add(failure); } }); } @@ -1969,7 +2004,9 @@ public class PackWriter implements AutoCloseable { byte[] pathBuf = walker.getPathBuffer(); int pathLen = walker.getPathLength(); bases.addBase(o.getType(), pathBuf, pathLen, pathHash); - filterAndAddObject(o, o.getType(), pathHash, want); + if (!depthSkip(o, walker)) { + filterAndAddObject(o, o.getType(), pathHash, want); + } countingMonitor.update(1); } } else { @@ -1979,7 +2016,10 @@ public class PackWriter implements AutoCloseable { continue; if (exclude(o)) continue; - filterAndAddObject(o, o.getType(), walker.getPathHashCode(), want); + if (!depthSkip(o, walker)) { + filterAndAddObject(o, o.getType(), walker.getPathHashCode(), + want); + } countingMonitor.update(1); } } @@ -2071,6 +2111,44 @@ public class PackWriter implements AutoCloseable { objectsMap.add(otp); } + /** + * Determines if the object should be omitted from the pack as a result of + * its depth (probably because of the tree:<depth> filter). + * <p> + * Causes {@code walker} to skip traversing the current tree, which ought to + * have just started traversal, assuming this method is called as soon as a + * new depth is reached. + * <p> + * This method increments the {@code treesTraversed} statistic. + * + * @param obj + * the object to check whether it should be omitted. + * @param walker + * the walker being used for traveresal. + * @return whether the given object should be skipped. + */ + private boolean depthSkip(@NonNull RevObject obj, ObjectWalk walker) { + long treeDepth = walker.getTreeDepth(); + + // Check if this object needs to be rejected because it is a tree or + // blob that is too deep from the root tree. + + // A blob is considered one level deeper than the tree that contains it. + if (obj.getType() == OBJ_BLOB) { + treeDepth++; + } else { + stats.treesTraversed++; + } + + if (filterSpec.getTreeDepthLimit() < 0 || + treeDepth <= filterSpec.getTreeDepthLimit()) { + return false; + } + + walker.skipTree(); + return true; + } + // Adds the given object as an object to be packed, first performing // filtering on blobs at or exceeding a given size. private void filterAndAddObject(@NonNull AnyObjectId src, int type, @@ -2079,10 +2157,10 @@ public class PackWriter implements AutoCloseable { // Check if this object needs to be rejected, doing the cheaper // checks first. - boolean reject = filterBlobLimit >= 0 && + boolean reject = filterSpec.getBlobLimit() >= 0 && type == OBJ_BLOB && !want.contains(src) && - reader.getObjectSize(src, OBJ_BLOB) > filterBlobLimit; + reader.getObjectSize(src, OBJ_BLOB) > filterSpec.getBlobLimit(); if (!reject) { addObject(src, type, pathHashCode); } @@ -2120,7 +2198,7 @@ public class PackWriter implements AutoCloseable { if (!cachedPacks.isEmpty()) { if (otp.isEdge()) return; - if ((nFmt == PACK_WHOLE) | (nFmt == PACK_DELTA)) { + if (nFmt == PACK_WHOLE || nFmt == PACK_DELTA) { for (CachedPack pack : cachedPacks) { if (pack.hasObject(otp, next)) { otp.setEdge(); @@ -2163,7 +2241,7 @@ public class PackWriter implements AutoCloseable { otp.clearReuseAsIs(); } - otp.setDeltaAttempted(reuseDeltas & next.wasDeltaAttempted()); + otp.setDeltaAttempted(reuseDeltas && next.wasDeltaAttempted()); otp.select(next); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java index 99db74956c..d3b5e128d1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java @@ -91,12 +91,9 @@ class PackWriterBitmapPreparer { private static final int DAY_IN_SECONDS = 24 * 60 * 60; - private static final Comparator<RevCommit> ORDER_BY_REVERSE_TIMESTAMP = new Comparator<RevCommit>() { - @Override - public int compare(RevCommit a, RevCommit b) { - return Integer.signum(b.getCommitTime() - a.getCommitTime()); - } - }; + private static final Comparator<RevCommit> ORDER_BY_REVERSE_TIMESTAMP = ( + RevCommit a, RevCommit b) -> Integer + .signum(b.getCommitTime() - a.getCommitTime()); private final ObjectReader reader; private final ProgressMonitor pm; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java new file mode 100644 index 0000000000..e16adb9bfb --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java @@ -0,0 +1,488 @@ +/* + * Copyright (C) 2018, Konrad Windszus <konrad_w@gmx.de> + * 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.transport.http; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.StringReader; +import java.io.Writer; +import java.net.HttpCookie; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.internal.storage.file.FileSnapshot; +import org.eclipse.jgit.internal.storage.file.LockFile; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.util.FileUtils; +import org.eclipse.jgit.util.IO; +import org.eclipse.jgit.util.RawParseUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Wraps all cookies persisted in a <strong>Netscape Cookie File Format</strong> + * being referenced via the git config <a href= + * "https://git-scm.com/docs/git-config#git-config-httpcookieFile">http.cookieFile</a>. + * <p> + * It will only load the cookies lazily, i.e. before calling + * {@link #getCookies(boolean)} the file is not evaluated. This class also + * allows persisting cookies in that file format. + * <p> + * In general this class is not thread-safe. So any consumer needs to take care + * of synchronization! + * + * @see <a href="http://www.cookiecentral.com/faq/#3.5">Netscape Cookie File + * Format</a> + * @see <a href= + * "https://unix.stackexchange.com/questions/36531/format-of-cookies-when-using-wget">Cookie + * format for wget</a> + * @see <a href= + * "https://github.com/curl/curl/blob/07ebaf837843124ee670e5b8c218b80b92e06e47/lib/cookie.c#L745">libcurl + * Cookie file parsing</a> + * @see <a href= + * "https://github.com/curl/curl/blob/07ebaf837843124ee670e5b8c218b80b92e06e47/lib/cookie.c#L1417">libcurl + * Cookie file writing</a> + * @see NetscapeCookieFileCache + */ +public final class NetscapeCookieFile { + + private static final String HTTP_ONLY_PREAMBLE = "#HttpOnly_"; //$NON-NLS-1$ + + private static final String COLUMN_SEPARATOR = "\t"; //$NON-NLS-1$ + + private static final String LINE_SEPARATOR = "\n"; //$NON-NLS-1$ + + /** + * Maximum number of retries to acquire the lock for writing to the + * underlying file. + */ + private static final int LOCK_ACQUIRE_MAX_RETRY_COUNT = 4; + + /** + * Sleep time in milliseconds between retries to acquire the lock for + * writing to the underlying file. + */ + private static final int LOCK_ACQUIRE_RETRY_SLEEP = 500; + + private final Path path; + + private FileSnapshot snapshot; + + private byte[] hash; + + final Date creationDate; + + private Set<HttpCookie> cookies = null; + + private static final Logger LOG = LoggerFactory + .getLogger(NetscapeCookieFile.class); + + /** + * @param path + * where to find the cookie file + */ + public NetscapeCookieFile(Path path) { + this(path, new Date()); + } + + NetscapeCookieFile(Path path, Date creationDate) { + this.path = path; + this.snapshot = FileSnapshot.DIRTY; + this.creationDate = creationDate; + } + + /** + * Path to the underlying cookie file. + * + * @return the path + */ + public Path getPath() { + return path; + } + + /** + * Return all cookies from the underlying cookie file. + * + * @param refresh + * if {@code true} updates the list from the underlying cookie + * file if it has been modified since the last read otherwise + * returns the current transient state. In case the cookie file + * has never been read before will always read from the + * underlying file disregarding the value of this parameter. + * @return all cookies (may contain session cookies as well). This does not + * return a copy of the list but rather the original one. Every + * addition to the returned list can afterwards be persisted via + * {@link #write(URL)}. Errors in the underlying file will not lead + * to exceptions but rather to an empty set being returned and the + * underlying error being logged. + */ + public Set<HttpCookie> getCookies(boolean refresh) { + if (cookies == null || refresh) { + try { + byte[] in = getFileContentIfModified(); + Set<HttpCookie> newCookies = parseCookieFile(in, creationDate); + if (cookies != null) { + cookies = mergeCookies(newCookies, cookies); + } else { + cookies = newCookies; + } + return cookies; + } catch (IOException | IllegalArgumentException e) { + LOG.warn( + MessageFormat.format( + JGitText.get().couldNotReadCookieFile, path), + e); + if (cookies == null) { + cookies = new LinkedHashSet<>(); + } + } + } + return cookies; + + } + + /** + * Parses the given file and extracts all cookie information from it. + * + * @param input + * the file content to parse + * @param creationDate + * the date for the creation of the cookies (used to calculate + * the maxAge based on the expiration date given within the file) + * @return the set of parsed cookies from the given file (even expired + * ones). If there is more than one cookie with the same name in + * this file the last one overwrites the first one! + * @throws IOException + * if the given file could not be read for some reason + * @throws IllegalArgumentException + * if the given file does not have a proper format + */ + private static Set<HttpCookie> parseCookieFile(@NonNull byte[] input, + @NonNull Date creationDate) + throws IOException, IllegalArgumentException { + + String decoded = RawParseUtils.decode(StandardCharsets.US_ASCII, input); + + Set<HttpCookie> cookies = new LinkedHashSet<>(); + try (BufferedReader reader = new BufferedReader( + new StringReader(decoded))) { + String line; + while ((line = reader.readLine()) != null) { + HttpCookie cookie = parseLine(line, creationDate); + if (cookie != null) { + cookies.add(cookie); + } + } + } + return cookies; + } + + private static HttpCookie parseLine(@NonNull String line, + @NonNull Date creationDate) { + if (line.isEmpty() || (line.startsWith("#") //$NON-NLS-1$ + && !line.startsWith(HTTP_ONLY_PREAMBLE))) { + return null; + } + String[] cookieLineParts = line.split(COLUMN_SEPARATOR, 7); + if (cookieLineParts == null) { + throw new IllegalArgumentException(MessageFormat + .format(JGitText.get().couldNotFindTabInLine, line)); + } + if (cookieLineParts.length < 7) { + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().couldNotFindSixTabsInLine, + Integer.valueOf(cookieLineParts.length), line)); + } + String name = cookieLineParts[5]; + String value = cookieLineParts[6]; + HttpCookie cookie = new HttpCookie(name, value); + + String domain = cookieLineParts[0]; + if (domain.startsWith(HTTP_ONLY_PREAMBLE)) { + cookie.setHttpOnly(true); + domain = domain.substring(HTTP_ONLY_PREAMBLE.length()); + } + // strip off leading "." + // (https://tools.ietf.org/html/rfc6265#section-5.2.3) + if (domain.startsWith(".")) { //$NON-NLS-1$ + domain = domain.substring(1); + } + cookie.setDomain(domain); + // domain evaluation as boolean flag not considered (i.e. always assumed + // to be true) + cookie.setPath(cookieLineParts[2]); + cookie.setSecure(Boolean.parseBoolean(cookieLineParts[3])); + + long expires = Long.parseLong(cookieLineParts[4]); + long maxAge = (expires - creationDate.getTime()) / 1000; + if (maxAge <= 0) { + return null; // skip expired cookies + } + cookie.setMaxAge(maxAge); + return cookie; + } + + /** + * Read the underying file and return its content but only in case it has + * been modified since the last access. + * <p> + * Internally calculates the hash and maintains {@link FileSnapshot}s to + * prevent issues described as <a href= + * "https://github.com/git/git/blob/master/Documentation/technical/racy-git.txt">"Racy + * Git problem"</a>. Inspired by {@link FileBasedConfig#load()}. + * + * @return the file contents in case the file has been modified since the + * last access, otherwise {@code null} + * @throws IOException + * if the file is not found or cannot be read + */ + private byte[] getFileContentIfModified() throws IOException { + final int maxStaleRetries = 5; + int retries = 0; + File file = getPath().toFile(); + if (!file.exists()) { + LOG.warn(MessageFormat.format(JGitText.get().missingCookieFile, + file.getAbsolutePath())); + return new byte[0]; + } + while (true) { + final FileSnapshot oldSnapshot = snapshot; + final FileSnapshot newSnapshot = FileSnapshot.save(file); + try { + final byte[] in = IO.readFully(file); + byte[] newHash = hash(in); + if (Arrays.equals(hash, newHash)) { + if (oldSnapshot.equals(newSnapshot)) { + oldSnapshot.setClean(newSnapshot); + } else { + snapshot = newSnapshot; + } + } else { + snapshot = newSnapshot; + hash = newHash; + } + return in; + } catch (FileNotFoundException e) { + throw e; + } catch (IOException e) { + if (FileUtils.isStaleFileHandle(e) + && retries < maxStaleRetries) { + if (LOG.isDebugEnabled()) { + LOG.debug(MessageFormat.format( + JGitText.get().configHandleIsStale, + Integer.valueOf(retries)), e); + } + retries++; + continue; + } + throw new IOException(MessageFormat + .format(JGitText.get().cannotReadFile, getPath()), e); + } + } + + } + + private static byte[] hash(final byte[] in) { + return Constants.newMessageDigest().digest(in); + } + + /** + * Writes all the cookies being maintained in the set being returned by + * {@link #getCookies(boolean)} to the underlying file. + * <p> + * Session-cookies will not be persisted. + * + * @param url + * url for which to write the cookies (important to derive + * default values for non-explicitly set attributes) + * @throws IOException + * if the underlying cookie file could not be read or written or + * a problem with the lock file + * @throws InterruptedException + * if the thread is interrupted while waiting for the lock + */ + public void write(URL url) throws IOException, InterruptedException { + try { + byte[] cookieFileContent = getFileContentIfModified(); + if (cookieFileContent != null) { + LOG.debug("Reading the underlying cookie file '{}' " //$NON-NLS-1$ + + "as it has been modified since " //$NON-NLS-1$ + + "the last access", //$NON-NLS-1$ + path); + // reread new changes if necessary + Set<HttpCookie> cookiesFromFile = NetscapeCookieFile + .parseCookieFile(cookieFileContent, creationDate); + this.cookies = mergeCookies(cookiesFromFile, cookies); + } + } catch (FileNotFoundException e) { + // ignore if file previously did not exist yet! + } + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + try (Writer writer = new OutputStreamWriter(output, + StandardCharsets.US_ASCII)) { + write(writer, cookies, url, creationDate); + } + LockFile lockFile = new LockFile(path.toFile()); + for (int retryCount = 0; retryCount < LOCK_ACQUIRE_MAX_RETRY_COUNT; retryCount++) { + if (lockFile.lock()) { + try { + lockFile.setNeedSnapshot(true); + lockFile.write(output.toByteArray()); + if (!lockFile.commit()) { + throw new IOException(MessageFormat.format( + JGitText.get().cannotCommitWriteTo, path)); + } + } finally { + lockFile.unlock(); + } + return; + } + Thread.sleep(LOCK_ACQUIRE_RETRY_SLEEP); + } + throw new IOException( + MessageFormat.format(JGitText.get().cannotLock, lockFile)); + } + + /** + * Writes the given cookies to the file in the Netscape Cookie File Format + * (also used by curl). + * + * @param writer + * the writer to use to persist the cookies + * @param cookies + * the cookies to write into the file + * @param url + * the url for which to write the cookie (to derive the default + * values for certain cookie attributes) + * @param creationDate + * the date when the cookie has been created. Important for + * calculation the cookie expiration time (calculated from + * cookie's maxAge and this creation time) + * @throws IOException + * if an I/O error occurs + */ + static void write(@NonNull Writer writer, + @NonNull Collection<HttpCookie> cookies, @NonNull URL url, + @NonNull Date creationDate) throws IOException { + for (HttpCookie cookie : cookies) { + writeCookie(writer, cookie, url, creationDate); + } + } + + private static void writeCookie(@NonNull Writer writer, + @NonNull HttpCookie cookie, @NonNull URL url, + @NonNull Date creationDate) throws IOException { + if (cookie.getMaxAge() <= 0) { + return; // skip expired cookies + } + String domain = ""; //$NON-NLS-1$ + if (cookie.isHttpOnly()) { + domain = HTTP_ONLY_PREAMBLE; + } + if (cookie.getDomain() != null) { + domain += cookie.getDomain(); + } else { + domain += url.getHost(); + } + writer.write(domain); + writer.write(COLUMN_SEPARATOR); + writer.write("TRUE"); //$NON-NLS-1$ + writer.write(COLUMN_SEPARATOR); + String path = cookie.getPath(); + if (path == null) { + path = url.getPath(); + } + writer.write(path); + writer.write(COLUMN_SEPARATOR); + writer.write(Boolean.toString(cookie.getSecure()).toUpperCase()); + writer.write(COLUMN_SEPARATOR); + final String expirationDate; + // whenCreated field is not accessible in HttpCookie + expirationDate = String + .valueOf(creationDate.getTime() + (cookie.getMaxAge() * 1000)); + writer.write(expirationDate); + writer.write(COLUMN_SEPARATOR); + writer.write(cookie.getName()); + writer.write(COLUMN_SEPARATOR); + writer.write(cookie.getValue()); + writer.write(LINE_SEPARATOR); + } + + /** + * Merge the given sets in the following way. All cookies from + * {@code cookies1} and {@code cookies2} are contained in the resulting set + * which have unique names. If there is a duplicate entry for one name only + * the entry from set {@code cookies1} ends up in the resulting set. + * + * @param cookies1 + * first set of cookies + * @param cookies2 + * second set of cookies + * + * @return the merged cookies + */ + static Set<HttpCookie> mergeCookies(Set<HttpCookie> cookies1, + @Nullable Set<HttpCookie> cookies2) { + Set<HttpCookie> mergedCookies = new LinkedHashSet<>(cookies1); + if (cookies2 != null) { + mergedCookies.addAll(cookies2); + } + return mergedCookies; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java new file mode 100644 index 0000000000..882b2d055b --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2018, Konrad Windszus <konrad_w@gmx.de> + * 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.transport.http; + +import java.nio.file.Path; + +import org.eclipse.jgit.transport.HttpConfig; +import org.eclipse.jgit.util.LRUMap; + +/** + * A cache of all known cookie files ({@link NetscapeCookieFile}). May contain + * at most {@code n} entries, where the least-recently used one is evicted as + * soon as more entries are added. The maximum number of entries (={@code n}) + * can be set via the git config key {@code http.cookieFileCacheLimit}. By + * default it is set to 10. + * <p> + * The cache is global, i.e. it is shared among all consumers within the same + * Java process. + * + * @see NetscapeCookieFile + * + */ +public class NetscapeCookieFileCache { + + private final LRUMap<Path, NetscapeCookieFile> cookieFileMap; + + private static NetscapeCookieFileCache instance; + + private NetscapeCookieFileCache(HttpConfig config) { + cookieFileMap = new LRUMap<>(config.getCookieFileCacheLimit(), + config.getCookieFileCacheLimit()); + } + + /** + * @param config + * the config which defines the limit for this cache + * @return the singleton instance of the cookie file cache. If the cache has + * already been created the given config is ignored (even if it + * differs from the config, with which the cache has originally been + * created) + */ + public static NetscapeCookieFileCache getInstance(HttpConfig config) { + if (instance == null) { + return new NetscapeCookieFileCache(config); + } else { + return instance; + } + } + + /** + * @param path + * the path of the cookie file to retrieve + * @return the cache entry belonging to the requested file + */ + public NetscapeCookieFile getEntry(Path path) { + if (!cookieFileMap.containsKey(path)) { + synchronized (NetscapeCookieFileCache.class) { + if (!cookieFileMap.containsKey(path)) { + cookieFileMap.put(path, new NetscapeCookieFile(path)); + } + } + } + return cookieFileMap.get(path); + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java index d105d0d200..b0339c677f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java @@ -162,7 +162,7 @@ public final class AbbreviatedObjectId implements Serializable { r |= RawParseUtils.parseHexInt4(bs[p++]); n++; } - return r << (8 - n) * 4; + return r << ((8 - n) * 4); } static int mask(int nibbles, int word, int v) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java index 978dd3a729..4f90e69008 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java @@ -49,6 +49,7 @@ import java.io.Writer; import java.nio.ByteBuffer; import org.eclipse.jgit.util.NB; +import org.eclipse.jgit.util.References; /** * A (possibly mutable) SHA-1 abstraction. @@ -60,19 +61,37 @@ import org.eclipse.jgit.util.NB; public abstract class AnyObjectId implements Comparable<AnyObjectId> { /** - * Compare to object identifier byte sequences for equality. + * Compare two object identifier byte sequences for equality. * * @param firstObjectId * the first identifier to compare. Must not be null. * @param secondObjectId * the second identifier to compare. Must not be null. * @return true if the two identifiers are the same. + * @deprecated use {@link #isEqual(AnyObjectId, AnyObjectId)} instead */ + @Deprecated + @SuppressWarnings("AmbiguousMethodReference") public static boolean equals(final AnyObjectId firstObjectId, final AnyObjectId secondObjectId) { - if (firstObjectId == secondObjectId) - return true; + return isEqual(firstObjectId, secondObjectId); + } + /** + * Compare two object identifier byte sequences for equality. + * + * @param firstObjectId + * the first identifier to compare. Must not be null. + * @param secondObjectId + * the second identifier to compare. Must not be null. + * @return true if the two identifiers are the same. + * @since 5.4 + */ + public static boolean isEqual(final AnyObjectId firstObjectId, + final AnyObjectId secondObjectId) { + if (References.isSameObject(firstObjectId, secondObjectId)) { + return true; + } // We test word 3 first since the git file-based ODB // uses the first byte of w1, and we use w2 as the // hash code, one of those probably came up with these @@ -80,7 +99,6 @@ public abstract class AnyObjectId implements Comparable<AnyObjectId> { // Therefore the first two words are very likely to be // identical. We want to break away from collisions as // quickly as possible. - // return firstObjectId.w3 == secondObjectId.w3 && firstObjectId.w4 == secondObjectId.w4 && firstObjectId.w5 == secondObjectId.w5 @@ -276,8 +294,9 @@ public abstract class AnyObjectId implements Comparable<AnyObjectId> { * the other id to compare to. May be null. * @return true only if both ObjectIds have identical bits. */ + @SuppressWarnings({ "NonOverridingEquals", "AmbiguousMethodReference" }) public final boolean equals(AnyObjectId other) { - return other != null ? equals(this, other) : false; + return other != null ? isEqual(this, other) : false; } /** {@inheritDoc} */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java index 6cbddec543..13f71a7ff6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java @@ -57,6 +57,7 @@ import java.text.MessageFormat; import java.util.List; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.util.References; /** * Mutable builder to construct a commit recording the state of a project. @@ -365,7 +366,7 @@ public class CommitBuilder { os.write('\n'); } - if (getEncoding() != UTF_8) { + if (!References.isSameObject(getEncoding(), UTF_8)) { os.write(hencoding); os.write(' '); os.write(Constants.encodeASCII(getEncoding().name())); @@ -474,7 +475,7 @@ public class CommitBuilder { r.append(gpgSignature != null ? gpgSignature.toString() : "NOT_SET"); r.append("\n"); - if (encoding != null && encoding != UTF_8) { + if (encoding != null && !References.isSameObject(encoding, UTF_8)) { r.append("encoding "); r.append(encoding.name()); r.append("\n"); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java index 6ab5c149ff..16db717032 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java @@ -107,7 +107,7 @@ public class Config { * must ensure it is a special copy of the empty string. It also must * be treated like the empty string. */ - static final String MAGIC_EMPTY_VALUE = new String(); + private static final String MISSING_ENTRY = new String(); /** * Create a configuration with no default fallback. @@ -129,6 +129,18 @@ public class Config { } /** + * Check if a given string is the "missing" value. + * + * @param value string to be checked. + * @return true if the given string is the "missing" value. + * @since 5.4 + */ + @SuppressWarnings({ "ReferenceEquality", "StringEquality" }) + public static boolean isMissing(String value) { + return value == MISSING_ENTRY; + } + + /** * Globally sets a {@link org.eclipse.jgit.lib.TypedConfigGetter} that is * subsequently used to read typed values from all git configs. * @@ -1041,7 +1053,7 @@ public class Config { if (e.prefix == null || "".equals(e.prefix)) //$NON-NLS-1$ out.append('\t'); out.append(e.name); - if (MAGIC_EMPTY_VALUE != e.value) { + if (!isMissing(e.value)) { out.append(" ="); //$NON-NLS-1$ if (e.value != null) { out.append(' '); @@ -1132,7 +1144,7 @@ public class Config { e.name = readKeyName(in); if (e.name.endsWith("\n")) { //$NON-NLS-1$ e.name = e.name.substring(0, e.name.length() - 1); - e.value = MAGIC_EMPTY_VALUE; + e.value = MISSING_ENTRY; } else e.value = readValue(in); @@ -1165,7 +1177,7 @@ public class Config { private void addIncludedConfig(final List<ConfigLine> newEntries, ConfigLine line, int depth) throws ConfigInvalidException { if (!line.name.equalsIgnoreCase("path") || //$NON-NLS-1$ - line.value == null || line.value.equals(MAGIC_EMPTY_VALUE)) { + line.value == null || line.value.equals(MISSING_ENTRY)) { throw new ConfigInvalidException(MessageFormat.format( JGitText.get().invalidLineInConfigFileWithParam, line)); } @@ -1413,11 +1425,23 @@ public class Config { case '"': value.append('"'); continue; + case '\r': { + int next = in.read(); + if (next == '\n') { + continue; // CR-LF + } else if (next >= 0) { + in.reset(); + } + break; + } default: - throw new ConfigInvalidException(MessageFormat.format( - JGitText.get().badEscape, - Character.valueOf(((char) c)))); + break; } + throw new ConfigInvalidException( + MessageFormat.format(JGitText.get().badEscape, + Character.isAlphabetic(c) + ? Character.valueOf(((char) c)) + : toUnicodeLiteral(c))); } if ('"' == c) { @@ -1430,6 +1454,11 @@ public class Config { return value.length() > 0 ? value.toString() : null; } + private static String toUnicodeLiteral(int c) { + return String.format("\\u%04x", //$NON-NLS-1$ + Integer.valueOf(c)); + } + /** * Parses a section of the configuration into an application model object. * <p> diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java index c72395fb69..078bf786f2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java @@ -395,50 +395,50 @@ public final class ConfigConstants { public static final String CONFIG_KEY_CHECKSTAT = "checkstat"; /** - * The "renamelimit" key in the "diff section" + * The "renamelimit" key in the "diff" section * @since 3.0 */ public static final String CONFIG_KEY_RENAMELIMIT = "renamelimit"; /** - * The "trustfolderstat" key in the "core section" + * The "trustfolderstat" key in the "core" section * @since 3.6 */ public static final String CONFIG_KEY_TRUSTFOLDERSTAT = "trustfolderstat"; /** - * The "supportsAtomicFileCreation" key in the "core section" + * The "supportsAtomicFileCreation" key in the "core" section * * @since 4.5 */ public static final String CONFIG_KEY_SUPPORTSATOMICFILECREATION = "supportsatomicfilecreation"; /** - * The "noprefix" key in the "diff section" + * The "noprefix" key in the "diff" section * @since 3.0 */ public static final String CONFIG_KEY_NOPREFIX = "noprefix"; /** - * A "renamelimit" value in the "diff section" + * A "renamelimit" value in the "diff" section * @since 3.0 */ public static final String CONFIG_RENAMELIMIT_COPY = "copy"; /** - * A "renamelimit" value in the "diff section" + * A "renamelimit" value in the "diff" section * @since 3.0 */ public static final String CONFIG_RENAMELIMIT_COPIES = "copies"; /** - * The "renames" key in the "diff section" + * The "renames" key in the "diff" section * @since 3.0 */ public static final String CONFIG_KEY_RENAMES = "renames"; /** - * The "inCoreLimit" key in the "merge section". It's a size limit (bytes) used to + * The "inCoreLimit" key in the "merge" section. It's a size limit (bytes) used to * control a file to be stored in {@code Heap} or {@code LocalFile} during the merge. * @since 4.9 */ 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 4c55196961..8f4468eef2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -58,7 +58,7 @@ import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.util.MutableInteger; /** - * Misc. constants used throughout JGit. + * Misc. constants and helpers used throughout JGit. */ @SuppressWarnings("nls") public final class Constants { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java index fb239399ed..e865da83b1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java @@ -72,7 +72,7 @@ public class DefaultTypedConfigGetter implements TypedConfigGetter { if (n == null) { return defaultValue; } - if (Config.MAGIC_EMPTY_VALUE == n) { + if (Config.isMissing(n)) { return true; } try { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java index d4c4d5b40d..8fa8d5f7d5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java @@ -88,6 +88,7 @@ public abstract class FileMode { public static final FileMode TREE = new FileMode(TYPE_TREE, Constants.OBJ_TREE) { @Override + @SuppressWarnings("NonOverridingEquals") public boolean equals(int modeBits) { return (modeBits & TYPE_MASK) == TYPE_TREE; } @@ -97,6 +98,7 @@ public abstract class FileMode { public static final FileMode SYMLINK = new FileMode(TYPE_SYMLINK, Constants.OBJ_BLOB) { @Override + @SuppressWarnings("NonOverridingEquals") public boolean equals(int modeBits) { return (modeBits & TYPE_MASK) == TYPE_SYMLINK; } @@ -106,6 +108,7 @@ public abstract class FileMode { public static final FileMode REGULAR_FILE = new FileMode(0100644, Constants.OBJ_BLOB) { @Override + @SuppressWarnings("NonOverridingEquals") public boolean equals(int modeBits) { return (modeBits & TYPE_MASK) == TYPE_FILE && (modeBits & 0111) == 0; } @@ -115,6 +118,7 @@ public abstract class FileMode { public static final FileMode EXECUTABLE_FILE = new FileMode(0100755, Constants.OBJ_BLOB) { @Override + @SuppressWarnings("NonOverridingEquals") public boolean equals(int modeBits) { return (modeBits & TYPE_MASK) == TYPE_FILE && (modeBits & 0111) != 0; } @@ -124,6 +128,7 @@ public abstract class FileMode { public static final FileMode GITLINK = new FileMode(TYPE_GITLINK, Constants.OBJ_COMMIT) { @Override + @SuppressWarnings("NonOverridingEquals") public boolean equals(int modeBits) { return (modeBits & TYPE_MASK) == TYPE_GITLINK; } @@ -133,6 +138,7 @@ public abstract class FileMode { public static final FileMode MISSING = new FileMode(TYPE_MISSING, Constants.OBJ_BAD) { @Override + @SuppressWarnings("NonOverridingEquals") public boolean equals(int modeBits) { return modeBits == 0; } @@ -165,6 +171,7 @@ public abstract class FileMode { return new FileMode(bits, Constants.OBJ_BAD) { @Override + @SuppressWarnings("NonOverridingEquals") public boolean equals(int a) { return bits == a; } @@ -206,6 +213,7 @@ public abstract class FileMode { * a int. * @return true if the mode bits represent the same mode as this object */ + @SuppressWarnings("NonOverridingEquals") public abstract boolean equals(int modebits); /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java index f37c310752..ce1eb597fc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java @@ -352,12 +352,7 @@ public class IndexDiff { public WorkingTreeIterator getWorkingTreeIterator(Repository repo); } - private WorkingTreeIteratorFactory wTreeIt = new WorkingTreeIteratorFactory() { - @Override - public WorkingTreeIterator getWorkingTreeIterator(Repository repo) { - return new FileTreeIterator(repo); - } - }; + private WorkingTreeIteratorFactory wTreeIt = FileTreeIterator::new; /** * Allows higher layers to set the factory for WorkingTreeIterators. @@ -647,11 +642,12 @@ public class IndexDiff { private void addConflict(String path, int stage) { StageState existingStageStates = conflicts.get(path); byte stageMask = 0; - if (existingStageStates != null) - stageMask |= existingStageStates.getStageMask(); + if (existingStageStates != null) { + stageMask |= (byte) existingStageStates.getStageMask(); + } // stage 1 (base) should be shifted 0 times int shifts = stage - 1; - stageMask |= (1 << shifts); + stageMask |= (byte) (1 << shifts); StageState stageState = StageState.fromMask(stageMask); conflicts.put(path, stageState); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java index b791c64552..d65c1bdf1a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java @@ -67,7 +67,7 @@ public abstract class ObjectIdRef implements Ref { */ public Unpeeled(@NonNull Storage st, @NonNull String name, @Nullable ObjectId id) { - super(st, name, id, -1); + super(st, name, id, UNDEFINED_UPDATE_INDEX); } /** @@ -119,7 +119,7 @@ public abstract class ObjectIdRef implements Ref { */ public PeeledTag(@NonNull Storage st, @NonNull String name, @Nullable ObjectId id, @NonNull ObjectId p) { - super(st, name, id, -1); + super(st, name, id, UNDEFINED_UPDATE_INDEX); peeledObjectId = p; } @@ -172,7 +172,7 @@ public abstract class ObjectIdRef implements Ref { */ public PeeledNonTag(@NonNull Storage st, @NonNull String name, @Nullable ObjectId id) { - super(st, name, id, -1); + super(st, name, id, UNDEFINED_UPDATE_INDEX); } /** @@ -284,7 +284,7 @@ public abstract class ObjectIdRef implements Ref { */ @Override public long getUpdateIndex() { - if (updateIndex == -1) { + if (updateIndex == UNDEFINED_UPDATE_INDEX) { throw new UnsupportedOperationException(); } return updateIndex; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSubclassMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSubclassMap.java index cd57bda82e..470275beb1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSubclassMap.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSubclassMap.java @@ -103,8 +103,9 @@ public class ObjectIdSubclassMap<V extends ObjectId> V obj; while ((obj = tbl[i]) != null) { - if (AnyObjectId.equals(obj, toFind)) + if (AnyObjectId.isEqual(obj, toFind)) { return obj; + } i = (i + 1) & msk; } return null; @@ -162,7 +163,7 @@ public class ObjectIdSubclassMap<V extends ObjectId> V obj; while ((obj = tbl[i]) != null) { - if (AnyObjectId.equals(obj, newValue)) + if (AnyObjectId.isEqual(obj, newValue)) return obj; i = (i + 1) & msk; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java index 0d31851836..ebbb3a4844 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java @@ -181,7 +181,7 @@ public class RebaseTodoFile { int nextSpace = RawParseUtils.next(buf, tokenBegin, ' '); int tokenCount = 0; - while (tokenCount < 3 && nextSpace < lineEnd) { + while (tokenCount < 3 && nextSpace <= lineEnd) { switch (tokenCount) { case 0: String actionToken = new String(buf, tokenBegin, @@ -193,8 +193,14 @@ public class RebaseTodoFile { break; case 1: nextSpace = RawParseUtils.next(buf, tokenBegin, ' '); - String commitToken = new String(buf, tokenBegin, - nextSpace - tokenBegin - 1, UTF_8); + String commitToken; + if (nextSpace > lineEnd + 1) { + commitToken = new String(buf, tokenBegin, + lineEnd - tokenBegin + 1, UTF_8); + } else { + commitToken = new String(buf, tokenBegin, + nextSpace - tokenBegin - 1, UTF_8); + } tokenBegin = nextSpace; commit = AbbreviatedObjectId.fromString(commitToken); break; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java index 32c8b06c91..4082d21c2e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java @@ -126,6 +126,13 @@ public interface Ref { } /** + * Update index value when a reference doesn't have one + * + * @since 5.4 + */ + long UNDEFINED_UPDATE_INDEX = -1L; + + /** * What this ref is called within the repository. * * @return name of this ref. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java index 877792097c..4d9450e758 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java @@ -44,6 +44,7 @@ package org.eclipse.jgit.lib; import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; import java.io.IOException; import java.util.ArrayList; @@ -52,7 +53,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; - +import java.util.Set; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.Nullable; @@ -470,6 +471,31 @@ public abstract class RefDatabase { return Collections.unmodifiableList(result); } + + /** + * Returns all refs that resolve directly to the given {@link ObjectId}. + * Includes peeled {@linkObjectId}s. This is the inverse lookup of + * {@link #exactRef(String...)}. + * + * <p> + * The default implementation uses a linear scan. Implementors of + * {@link RefDatabase} should override this method directly if a better + * implementation is possible. + * + * @param id + * {@link ObjectId} to resolve + * @return a {@link Set} of {@link Ref}s whose tips point to the provided + * id. + * @throws java.io.IOException + * the reference space cannot be accessed. + * @since 5.4 + */ + @NonNull + public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException { + return getRefs().stream().filter(r -> id.equals(r.getObjectId()) + || id.equals(r.getPeeledObjectId())).collect(toSet()); + } + /** * Check if any refs exist in the ref database. * <p> diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java index 1ce1528344..eca15c032a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java @@ -53,6 +53,7 @@ import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.transport.PushCertificate; +import org.eclipse.jgit.util.References; /** * Creates, updates or deletes any reference. @@ -599,6 +600,7 @@ public abstract class RefUpdate { */ public Result update() throws IOException { try (RevWalk rw = new RevWalk(getRepository())) { + rw.setRetainBody(false); return update(rw); } } @@ -646,6 +648,7 @@ public abstract class RefUpdate { */ public Result delete() throws IOException { try (RevWalk rw = new RevWalk(getRepository())) { + rw.setRetainBody(false); return delete(rw); } } @@ -751,7 +754,7 @@ public abstract class RefUpdate { if (expValue != null) { final ObjectId o; o = oldValue != null ? oldValue : ObjectId.zeroId(); - if (!AnyObjectId.equals(expValue, o)) { + if (!AnyObjectId.isEqual(expValue, o)) { return Result.LOCK_FAILURE; } } @@ -766,7 +769,8 @@ public abstract class RefUpdate { } oldObj = safeParseOld(walk, oldValue); - if (newObj == oldObj && !detachingSymbolicRef) { + if (References.isSameObject(newObj, oldObj) + && !detachingSymbolicRef) { return store.execute(Result.NO_CHANGE); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index a61897a652..68866ea279 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -240,6 +240,15 @@ public abstract class Repository implements AutoCloseable { } /** + * Get repository identifier. + * + * @return repository identifier. The returned identifier has to be unique + * within a given Git server. + * @since 5.4 + */ + public abstract String getIdentifier(); + + /** * Get the object database which stores this repository's data. * * @return the object database which stores this repository's data. @@ -489,6 +498,7 @@ public abstract class Repository implements AutoCloseable { throws AmbiguousObjectException, IncorrectObjectTypeException, RevisionSyntaxException, IOException { try (RevWalk rw = new RevWalk(this)) { + rw.setRetainBody(false); Object resolved = resolve(rw, revstr); if (resolved instanceof String) { final Ref ref = findRef((String) resolved); @@ -515,6 +525,7 @@ public abstract class Repository implements AutoCloseable { public String simplify(String revstr) throws AmbiguousObjectException, IOException { try (RevWalk rw = new RevWalk(this)) { + rw.setRetainBody(true); Object resolved = resolve(rw, revstr); if (resolved != null) if (resolved instanceof String) @@ -606,7 +617,7 @@ public abstract class Repository implements AutoCloseable { if (!(rev instanceof RevBlob)) throw new IncorrectObjectTypeException(rev, Constants.TYPE_BLOB); - } else if (item.equals("")) { //$NON-NLS-1$ + } else if (item.isEmpty()) { rev = rw.peel(rev); } else throw new RevisionSyntaxException(revstr); @@ -707,7 +718,7 @@ public abstract class Repository implements AutoCloseable { if (time.equals("upstream")) { //$NON-NLS-1$ if (name == null) name = new String(revChars, done, i); - if (name.equals("")) //$NON-NLS-1$ + if (name.isEmpty()) // Currently checked out branch, HEAD if // detached name = Constants.HEAD; @@ -762,7 +773,7 @@ public abstract class Repository implements AutoCloseable { } else { if (name == null) name = new String(revChars, done, i); - if (name.equals("")) //$NON-NLS-1$ + if (name.isEmpty()) name = Constants.HEAD; if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$ throw new RevisionSyntaxException(MessageFormat @@ -788,7 +799,7 @@ public abstract class Repository implements AutoCloseable { if (rev == null) { if (name == null) name = new String(revChars, done, i); - if (name.equals("")) //$NON-NLS-1$ + if (name.isEmpty()) name = Constants.HEAD; rev = parseSimple(rw, name); name = null; @@ -921,7 +932,7 @@ public abstract class Repository implements AutoCloseable { AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr); try (ObjectReader reader = newObjectReader()) { Collection<ObjectId> matches = reader.resolve(id); - if (matches.size() == 0) + if (matches.isEmpty()) return null; else if (matches.size() == 1) return matches.iterator().next(); @@ -1275,11 +1286,8 @@ public abstract class Repository implements AutoCloseable { CorruptObjectException, IOException { // we want DirCache to inform us so that we can inform registered // listeners about index changes - IndexChangedListener l = new IndexChangedListener() { - @Override - public void onIndexChanged(IndexChangedEvent event) { - notifyIndexChanged(true); - } + IndexChangedListener l = (IndexChangedEvent event) -> { + notifyIndexChanged(true); }; return DirCache.lock(this, l); } @@ -1528,19 +1536,22 @@ public abstract class Repository implements AutoCloseable { final String filePath = file.getPath(); final String workDirPath = workDir.getPath(); - if (filePath.length() <= workDirPath.length() || - filePath.charAt(workDirPath.length()) != File.separatorChar || - !filePath.startsWith(workDirPath)) { - File absWd = workDir.isAbsolute() ? workDir : workDir.getAbsoluteFile(); + if (filePath.length() <= workDirPath.length() + || filePath.charAt(workDirPath.length()) != File.separatorChar + || !filePath.startsWith(workDirPath)) { + File absWd = workDir.isAbsolute() ? workDir + : workDir.getAbsoluteFile(); File absFile = file.isAbsolute() ? file : file.getAbsoluteFile(); - if (absWd == workDir && absFile == file) + if (absWd.equals(workDir) && absFile.equals(file)) { return ""; //$NON-NLS-1$ + } return stripWorkDir(absWd, absFile); } String relName = filePath.substring(workDirPath.length() + 1); - if (File.separatorChar != '/') + if (File.separatorChar != '/') { relName = relName.replace(File.separatorChar, '/'); + } return relName; } 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 400342b1bd..27befba5a4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java @@ -255,14 +255,11 @@ public class RepositoryCache { if (delay == RepositoryCacheConfig.NO_CLEANUP) { return; } - cleanupTask = scheduler.scheduleWithFixedDelay(new Runnable() { - @Override - public void run() { - try { - cache.clearAllExpired(); - } catch (Throwable e) { - LOG.error(e.getMessage(), e); - } + cleanupTask = scheduler.scheduleWithFixedDelay(() -> { + try { + cache.clearAllExpired(); + } catch (Throwable e) { + LOG.error(e.getMessage(), e); } }, delay, delay, TimeUnit.MILLISECONDS); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java index 00fcf52037..9f0568f0c2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java @@ -71,7 +71,7 @@ public class SymbolicRef implements Ref { public SymbolicRef(@NonNull String refName, @NonNull Ref target) { this.name = refName; this.target = target; - this.updateIndex = -1; + this.updateIndex = UNDEFINED_UPDATE_INDEX; } /** @@ -155,7 +155,7 @@ public class SymbolicRef implements Ref { */ @Override public long getUpdateIndex() { - if (updateIndex == -1) { + if (updateIndex == UNDEFINED_UPDATE_INDEX) { throw new UnsupportedOperationException(); } return updateIndex; 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 091667db01..0d44317658 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 @@ -54,6 +54,8 @@ import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.text.MessageFormat; import java.util.Iterator; import java.util.Locale; @@ -67,6 +69,7 @@ import org.bouncycastle.gpg.keybox.KeyBox; import org.bouncycastle.gpg.keybox.KeyInformation; import org.bouncycastle.gpg.keybox.PublicKeyRingBlob; 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.PGPSecretKey; @@ -85,6 +88,8 @@ import org.eclipse.jgit.errors.UnsupportedCredentialItem; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.SystemReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Locates GPG keys from either <code>~/.gnupg/private-keys-v1.d</code> or @@ -92,6 +97,9 @@ import org.eclipse.jgit.util.SystemReader; */ class BouncyCastleGpgKeyLocator { + private static final Logger log = LoggerFactory + .getLogger(BouncyCastleGpgKeyLocator.class); + private static final Path GPG_DIRECTORY = findGpgDirectory(); private static final Path USER_KEYBOX_PATH = GPG_DIRECTORY @@ -157,11 +165,14 @@ class BouncyCastleGpgKeyLocator { private PGPSecretKey attemptParseSecretKey(Path keyFile, PGPDigestCalculatorProvider calculatorProvider, PBEProtectionRemoverFactory passphraseProvider, - PGPPublicKey publicKey) throws IOException { + PGPPublicKey publicKey) { try (InputStream in = newInputStream(keyFile)) { return new SExprParser(calculatorProvider).parseSecretKey( new BufferedInputStream(in), passphraseProvider, publicKey); - } catch (PGPException | ClassCastException e) { + } catch (IOException | PGPException | ClassCastException e) { + if (log.isDebugEnabled()) + log.debug("Ignoring unreadable file '{}': {}", keyFile, //$NON-NLS-1$ + e.getMessage(), e); return null; } } @@ -173,10 +184,11 @@ class BouncyCastleGpgKeyLocator { private PGPPublicKey findPublicKeyByKeyId(KeyBlob keyBlob) throws IOException { + String keyId = signingKey.toLowerCase(Locale.ROOT); for (KeyInformation keyInfo : keyBlob.getKeyInformation()) { - if (signingKey.toLowerCase(Locale.ROOT) - .equals(Hex.toHexString(keyInfo.getKeyID()) - .toLowerCase(Locale.ROOT))) { + String fingerprint = Hex.toHexString(keyInfo.getFingerprint()) + .toLowerCase(Locale.ROOT); + if (fingerprint.endsWith(keyId)) { return getFirstPublicKey(keyBlob); } } @@ -201,9 +213,12 @@ class BouncyCastleGpgKeyLocator { * @return publicKey the public key (maybe <code>null</code>) * @throws IOException * in case of problems reading the file + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException */ private PGPPublicKey findPublicKeyInKeyBox(Path keyboxFile) - throws IOException { + throws IOException, NoSuchAlgorithmException, + NoSuchProviderException { KeyBox keyBox = readKeyBoxFile(keyboxFile); for (KeyBlob keyBlob : keyBox.getKeyBlobs()) { if (keyBlob.getType() == BlobType.OPEN_PGP_BLOB) { @@ -227,15 +242,17 @@ class BouncyCastleGpgKeyLocator { * @return the secret key * @throws IOException * in case of issues reading key files + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException * @throws PGPException * in case of issues finding a key * @throws CanceledException * @throws URISyntaxException * @throws UnsupportedCredentialItem */ - public BouncyCastleGpgKey findSecretKey() - throws IOException, PGPException, CanceledException, - UnsupportedCredentialItem, URISyntaxException { + public BouncyCastleGpgKey findSecretKey() throws IOException, + NoSuchAlgorithmException, NoSuchProviderException, PGPException, + CanceledException, UnsupportedCredentialItem, URISyntaxException { if (exists(USER_KEYBOX_PATH)) { PGPPublicKey publicKey = // findPublicKeyInKeyBox(USER_KEYBOX_PATH); @@ -252,6 +269,10 @@ class BouncyCastleGpgKeyLocator { USER_PGP_LEGACY_SECRING_FILE); if (secretKey != null) { + if (!secretKey.isSigningKey()) { + throw new PGPException(MessageFormat.format( + JGitText.get().gpgNotASigningKey, signingKey)); + } return new BouncyCastleGpgKey(secretKey, USER_PGP_LEGACY_SECRING_FILE); } @@ -285,6 +306,10 @@ class BouncyCastleGpgKeyLocator { PGPSecretKey secretKey = attemptParseSecretKey(keyFile, calculatorProvider, passphraseProvider, publicKey); if (secretKey != null) { + if (!secretKey.isSigningKey()) { + throw new PGPException(MessageFormat.format( + JGitText.get().gpgNotASigningKey, signingKey)); + } return new BouncyCastleGpgKey(secretKey, userKeyboxPath); } } @@ -326,6 +351,7 @@ class BouncyCastleGpgKeyLocator { PGPUtil.getDecoderStream(new BufferedInputStream(in)), new JcaKeyFingerprintCalculator()); + String keyId = signingkey.toLowerCase(Locale.ROOT); Iterator<PGPSecretKeyRing> keyrings = pgpSec.getKeyRings(); while (keyrings.hasNext()) { PGPSecretKeyRing keyRing = keyrings.next(); @@ -336,8 +362,7 @@ class BouncyCastleGpgKeyLocator { String fingerprint = Hex .toHexString(key.getPublicKey().getFingerprint()) .toLowerCase(Locale.ROOT); - if (fingerprint - .endsWith(signingkey.toLowerCase(Locale.ROOT))) { + if (fingerprint.endsWith(keyId)) { return key; } // try user id @@ -359,14 +384,12 @@ class BouncyCastleGpgKeyLocator { .getPublicKey(); } - private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException { + private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException, + NoSuchAlgorithmException, NoSuchProviderException { KeyBox keyBox; try (InputStream in = new BufferedInputStream( newInputStream(keyboxFile))) { - // note: KeyBox constructor reads in the whole InputStream at once - // this code will change in 1.61 to - // either 'new BcKeyBox(in)' or 'new JcaKeyBoxBuilder().build(in)' - keyBox = new KeyBox(in, new JcaKeyFingerprintCalculator()); + keyBox = new JcaKeyBoxBuilder().build(in); } return keyBox; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java index 4d696dd9e7..cfe0931b47 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java @@ -45,6 +45,8 @@ package org.eclipse.jgit.lib.internal; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URISyntaxException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.Security; import org.bouncycastle.bcpg.ArmoredOutputStream; @@ -100,7 +102,8 @@ public class BouncyCastleGpgSigner extends GpgSigner { BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey, committer, passphrasePrompt); return gpgKey != null; - } catch (PGPException | IOException | URISyntaxException e) { + } catch (PGPException | IOException | NoSuchAlgorithmException + | NoSuchProviderException | URISyntaxException e) { return false; } } @@ -109,7 +112,8 @@ public class BouncyCastleGpgSigner extends GpgSigner { PersonIdent committer, BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt) throws CanceledException, UnsupportedCredentialItem, IOException, - PGPException, URISyntaxException { + NoSuchAlgorithmException, NoSuchProviderException, PGPException, + URISyntaxException { if (gpgSigningKey == null || gpgSigningKey.isEmpty()) { gpgSigningKey = committer.getEmailAddress(); } @@ -153,7 +157,8 @@ public class BouncyCastleGpgSigner extends GpgSigner { signatureGenerator.generate().encode(out); } commit.setGpgSignature(new GpgSignature(buffer.toByteArray())); - } catch (PGPException | IOException | URISyntaxException e) { + } catch (PGPException | IOException | NoSuchAlgorithmException + | NoSuchProviderException | URISyntaxException e) { throw new JGitInternalException(e.getMessage(), e); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java index dd42e43841..a77cb4ffb9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java @@ -86,6 +86,11 @@ public final class MergeAlgorithm { private final static Edit END_EDIT = new Edit(Integer.MAX_VALUE, Integer.MAX_VALUE); + @SuppressWarnings("ReferenceEquality") + private static boolean isEndEdit(Edit edit) { + return edit == END_EDIT; + } + /** * Does the three way merge between a common base and two sequences. * @@ -145,7 +150,7 @@ public final class MergeAlgorithm { // iterate over all edits from base to ours and from base to theirs // leave the loop when there are no edits more for ours or for theirs // (or both) - while (theirsEdit != END_EDIT || oursEdit != END_EDIT) { + while (!isEndEdit(theirsEdit) || !isEndEdit(oursEdit)) { if (oursEdit.getEndA() < theirsEdit.getBeginA()) { // something was changed in ours not overlapping with any change // from theirs. First add the common part in front of the edit diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java index 75334ddb0c..0b423fb5d4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -1182,7 +1182,7 @@ public class ResolveMerger extends ThreeWayMerger { * fail. */ public Map<String, MergeFailureReason> getFailingPaths() { - return (failingPaths.size() == 0) ? null : failingPaths; + return failingPaths.isEmpty() ? null : failingPaths; } /** @@ -1193,7 +1193,7 @@ public class ResolveMerger extends ThreeWayMerger { * otherwise */ public boolean failed() { - return failingPaths.size() > 0; + return !failingPaths.isEmpty(); } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java index fd425abf35..b437f635f5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.nls; +import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -92,14 +93,13 @@ class GlobalBundleCache { } TranslationBundle bundle = bundles.get(type); if (bundle == null) { - bundle = type.newInstance(); + bundle = type.getDeclaredConstructor().newInstance(); bundle.load(locale); bundles.put(type, bundle); } return (T) bundle; - } catch (InstantiationException e) { - throw new Error(e); - } catch (IllegalAccessException e) { + } catch (InstantiationException | IllegalAccessException + | InvocationTargetException | NoSuchMethodException e) { throw new Error(e); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/nls/TranslationBundle.java b/org.eclipse.jgit/src/org/eclipse/jgit/nls/TranslationBundle.java index cdd7be266f..c41fb416d5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/nls/TranslationBundle.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/nls/TranslationBundle.java @@ -182,9 +182,7 @@ public abstract class TranslationBundle { field.set(this, translatedText); } catch (MissingResourceException e) { throw new TranslationStringMissingException(bundleClass, locale, field.getName(), e); - } catch (IllegalArgumentException e) { - throw new Error(e); - } catch (IllegalAccessException e) { + } catch (IllegalArgumentException | IllegalAccessException e) { throw new Error(e); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java index 325ff4f268..ba7223b8f0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java @@ -295,14 +295,14 @@ public class NoteMapMerger { private static boolean sameNote(Note a, Note b) { if (a == null && b == null) return true; - return a != null && b != null && AnyObjectId.equals(a, b); + return a != null && b != null && AnyObjectId.isEqual(a, b); } private static boolean sameContent(Note a, Note b) { if (a == null && b == null) return true; return a != null && b != null - && AnyObjectId.equals(a.getData(), b.getData()); + && AnyObjectId.isEqual(a.getData(), b.getData()); } private static InMemoryNoteBucket addIfNotNull(InMemoryNoteBucket result, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java index d278132c61..74eec81209 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java @@ -109,12 +109,13 @@ public class CombinedHunkHeader extends HunkHeader { final MutableInteger ptr = new MutableInteger(); ptr.value = nextLF(buf, startOffset, ' '); - for (int n = 0; n < old.length; n++) { - old[n].startLine = -parseBase10(buf, ptr.value, ptr); - if (buf[ptr.value] == ',') - old[n].lineCount = parseBase10(buf, ptr.value + 1, ptr); - else - old[n].lineCount = 1; + for (CombinedOldImage o : old) { + o.startLine = -parseBase10(buf, ptr.value, ptr); + if (buf[ptr.value] == ',') { + o.lineCount = parseBase10(buf, ptr.value + 1, ptr); + } else { + o.lineCount = 1; + } } newStartLine = parseBase10(buf, ptr.value + 1, ptr); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/AbstractPlotRenderer.java b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/AbstractPlotRenderer.java index 58e2106fe2..b2f8d11921 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/AbstractPlotRenderer.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/AbstractPlotRenderer.java @@ -114,39 +114,39 @@ public abstract class AbstractPlotRenderer<TLane extends PlotLane, TColor> { drawLine(myColor, myLaneX, h, myLaneX, (h + dotSize) / 2, LINE_WIDTH); - for (int i = 0; i < commit.mergingLanes.length; i++) { - final TLane pLane = (TLane) commit.mergingLanes[i]; + for (PlotLane mergingLane : commit.mergingLanes) { + final TLane pLane = (TLane) mergingLane; final TColor pColor = laneColor(pLane); final int cx = laneC(pLane); - if (Math.abs(myLaneX - cx) > LANE_WIDTH) { final int ix; - if (myLaneX < cx) + if (myLaneX < cx) { ix = cx - LANE_WIDTH / 2; - else + } else { ix = cx + LANE_WIDTH / 2; + } drawLine(pColor, myLaneX, h / 2, ix, h / 2, LINE_WIDTH); drawLine(pColor, ix, h / 2, cx, h, LINE_WIDTH); } else drawLine(pColor, myLaneX, h / 2, cx, h, LINE_WIDTH); - maxCenter = Math.max(maxCenter, cx); } } if (commit.getChildCount() > 0) { - for (int i = 0; i < commit.forkingOffLanes.length; i++) { - final TLane childLane = (TLane) commit.forkingOffLanes[i]; + for (PlotLane forkingOffLane : commit.forkingOffLanes) { + final TLane childLane = (TLane) forkingOffLane; final TColor cColor = laneColor(childLane); final int cx = laneC(childLane); if (Math.abs(myLaneX - cx) > LANE_WIDTH) { final int ix; - if (myLaneX < cx) + if (myLaneX < cx) { ix = cx - LANE_WIDTH / 2; - else + } else { ix = cx + LANE_WIDTH / 2; + } drawLine(cColor, myLaneX, h / 2, ix, h / 2, LINE_WIDTH); drawLine(cColor, ix, h / 2, cx, 0, LINE_WIDTH); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java new file mode 100644 index 0000000000..14e95670aa --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java @@ -0,0 +1,136 @@ +/* + * 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.revwalk; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; + +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.internal.revwalk.AddToBitmapFilter; +import org.eclipse.jgit.lib.BitmapIndex; +import org.eclipse.jgit.lib.BitmapIndex.Bitmap; +import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder; +import org.eclipse.jgit.lib.ProgressMonitor; + +/** + * Calculate the bitmap indicating what other commits are reachable from certain + * commit. + * <p> + * This bitmap refers only to commits. For a bitmap with ALL objects reachable + * from certain object, see {@code BitmapWalker}. + */ +class BitmapCalculator { + + private final RevWalk walk; + private final BitmapIndex bitmapIndex; + + BitmapCalculator(RevWalk walk) throws IOException { + this.walk = walk; + this.bitmapIndex = requireNonNull( + walk.getObjectReader().getBitmapIndex()); + } + + /** + * Get the reachability bitmap from certain commit to other commits. + * <p> + * This will return a precalculated bitmap if available or walk building one + * until finding a precalculated bitmap (and returning the union). + * <p> + * Beware that the returned bitmap it is guaranteed to include ONLY the + * commits reachable from the initial commit. It COULD include other objects + * (because precalculated bitmaps have them) but caller shouldn't count on + * that. See {@link BitmapWalker} for a full reachability bitmap. + * + * @param start + * the commit. Use {@code walk.parseCommit(objectId)} to get this + * object from the id. + * @param pm + * progress monitor. Updated by one per commit browsed in the + * graph + * @return the bitmap of reachable commits (and maybe some extra objects) + * for the commit + * @throws MissingObjectException + * the supplied id doesn't exist + * @throws IncorrectObjectTypeException + * the supplied id doesn't refer to a commit or a tag + * @throws IOException + * if the walk cannot open a packfile or loose object + */ + BitmapBuilder getBitmap(RevCommit start, ProgressMonitor pm) + throws MissingObjectException, + IncorrectObjectTypeException, IOException { + Bitmap precalculatedBitmap = bitmapIndex.getBitmap(start); + if (precalculatedBitmap != null) { + return asBitmapBuilder(precalculatedBitmap); + } + + walk.reset(); + walk.sort(RevSort.TOPO); + walk.markStart(start); + // Unbounded walk. If the repo has bitmaps, it should bump into one at + // some point. + + BitmapBuilder bitmapResult = bitmapIndex.newBitmapBuilder(); + walk.setRevFilter(new AddToBitmapFilter(bitmapResult)); + while (walk.next() != null) { + // Iterate through all of the commits. The BitmapRevFilter does + // the work. + // + // filter.include returns true for commits that do not have + // a bitmap in bitmapIndex and are not reachable from a + // bitmap in bitmapIndex encountered earlier in the walk. + // Thus the number of commits returned by next() measures how + // much history was traversed without being able to make use + // of bitmaps. + pm.update(1); + } + + return bitmapResult; + } + + private BitmapBuilder asBitmapBuilder(Bitmap bitmap) { + return bitmapIndex.newBitmapBuilder().or(bitmap); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java new file mode 100644 index 0000000000..6e510f677b --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java @@ -0,0 +1,120 @@ +/* + * 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.revwalk; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder; +import org.eclipse.jgit.lib.NullProgressMonitor; + +/** + * Checks the reachability using bitmaps. + */ +class BitmappedReachabilityChecker implements ReachabilityChecker { + + private final RevWalk walk; + + /** + * @param walk + * walk on the repository to get or create the bitmaps for the + * commits. It must have bitmaps. + * @throws AssertionError + * runtime exception if walk is over a repository without + * bitmaps + * @throws IOException + * if the index or the object reader cannot be opened. + */ + public BitmappedReachabilityChecker(RevWalk walk) + throws IOException { + this.walk = walk; + if (walk.getObjectReader().getBitmapIndex() == null) { + throw new AssertionError( + "Trying to use bitmapped reachability check " //$NON-NLS-1$ + + "on a repository without bitmaps"); //$NON-NLS-1$ + } + } + + /** + * Check all targets are reachable from the starters. + * <p> + * In this implementation, it is recommended to put the most popular + * starters (e.g. refs/heads tips) at the beginning of the collection + */ + @Override + public Optional<RevCommit> areAllReachable(Collection<RevCommit> targets, + Collection<RevCommit> starters) throws MissingObjectException, + IncorrectObjectTypeException, IOException { + BitmapCalculator calculator = new BitmapCalculator(walk); + + /** + * Iterate over starters bitmaps and remove targets as they become + * reachable. + * + * Building the total starters bitmap has the same cost (iterating over + * all starters adding the bitmaps) and this gives us the chance to + * shorcut the loop. + * + * This is based on the assuption that most of the starters will have + * the reachability bitmap precalculated. If many require a walk, the + * walk.reset() could start to take too much time. + */ + List<RevCommit> remainingTargets = new ArrayList<>(targets); + for (RevCommit starter : starters) { + BitmapBuilder starterBitmap = calculator.getBitmap(starter, + NullProgressMonitor.INSTANCE); + remainingTargets.removeIf(starterBitmap::contains); + if (remainingTargets.isEmpty()) { + return Optional.empty(); + } + } + + return Optional.of(remainingTargets.get(0)); + } + +} 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 5154920393..5199a2927d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java @@ -108,12 +108,25 @@ class DepthGenerator extends Generator { // Begin by sucking out all of the source's commits, and // adding them to the pending queue + FIFORevQueue unshallowCommits = new FIFORevQueue(); for (;;) { RevCommit c = s.next(); if (c == null) break; - if (((DepthWalk.Commit) c).getDepth() == 0) + if (c.has(UNSHALLOW)) { + unshallowCommits.add(c); + } else if (((DepthWalk.Commit) c).getDepth() == 0) { pending.add(c); + } + } + // Move unshallow commits to the front so that the REINTERESTING flag + // carry over code is executed first. + for (;;) { + RevCommit c = unshallowCommits.next(); + if (c == null) { + break; + } + pending.unpop(c); } // Mark DEEPEN_NOT on all deepen-not commits and their ancestors. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FooterLine.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FooterLine.java index d6fed66702..84b6d2e481 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FooterLine.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FooterLine.java @@ -95,7 +95,7 @@ public final class FooterLine { for (int kPtr = 0; kPtr < len;) { byte b = buffer[bPtr++]; if ('A' <= b && b <= 'Z') - b += 'a' - 'A'; + b += (byte) ('a' - 'A'); if (b != kRaw[kPtr++]) return false; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java index fd578da333..b6c5810b32 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.revwalk; +import static java.util.Objects.requireNonNull; import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT; import static org.eclipse.jgit.lib.Constants.OBJ_TREE; @@ -97,6 +98,55 @@ public class ObjectWalk extends RevWalk { */ private static final int IN_PENDING = RevWalk.REWRITE; + /** + * When walking over a tree and blob graph, objects are usually marked as + * seen as they are visited and this "seen" status is checked upon the next + * visit. If they are already "seen" then they are not processed (returned + * by {@link ObjectWalk#nextObject()}) again. However, this behavior can be + * overridden by supplying a different implementation of this class. + * + * @since 5.4 + */ + public interface VisitationPolicy { + /** + * Whenever the rev or object walk reaches a Git object, if that object + * already exists as a RevObject, this method is called to determine if + * that object should be visited. + * + * @param o + * the object to check if it should be visited + * @return true if the object should be visited + */ + boolean shouldVisit(RevObject o); + + /** + * Called when an object is visited. + * + * @param o + * the object that was visited + */ + void visited(RevObject o); + } + + /** + * The default visitation policy: causes all objects to be visited exactly + * once. + * + * @since 5.4 + */ + public static final VisitationPolicy SIMPLE_VISITATION_POLICY = + new VisitationPolicy() { + @Override + public boolean shouldVisit(RevObject o) { + return (o.flags & SEEN) == 0; + } + + @Override + public void visited(RevObject o) { + o.flags |= SEEN; + } + }; + private List<RevObject> rootObjects; private BlockObjQueue pendingObjects; @@ -113,6 +163,8 @@ public class ObjectWalk extends RevWalk { private boolean boundary; + private VisitationPolicy visitationPolicy = SIMPLE_VISITATION_POLICY; + /** * Create a new revision and object walker for a given repository. * @@ -299,6 +351,18 @@ public class ObjectWalk extends RevWalk { objectFilter = newFilter != null ? newFilter : ObjectFilter.ALL; } + /** + * Sets the visitation policy to use during this walk. + * + * @param policy + * the {@code VisitationPolicy} to use + * @since 5.4 + */ + public void setVisitationPolicy(VisitationPolicy policy) { + assertNotStarted(); + visitationPolicy = requireNonNull(policy); + } + /** {@inheritDoc} */ @Override public RevCommit next() throws MissingObjectException, @@ -326,6 +390,17 @@ public class ObjectWalk extends RevWalk { } /** + * Skips the current tree such that {@link #nextObject()} does not return + * any objects inside it. This should be called right after + * {@link #nextObject()} returns the tree. + * + * @since 5.4 + */ + public void skipTree() { + currVisit.ptr = currVisit.buf.length; + } + + /** * Pop the next most recent object. * * @return next most recent object; null if traversal is over. @@ -357,24 +432,23 @@ public class ObjectWalk extends RevWalk { } RevObject obj = objects.get(idBuffer); - if (obj != null && (obj.flags & SEEN) != 0) + if (obj != null && !visitationPolicy.shouldVisit(obj)) continue; int mode = parseMode(buf, startPtr, ptr, tv); - int flags; switch (mode >>> TYPE_SHIFT) { case TYPE_FILE: case TYPE_SYMLINK: if (obj == null) { obj = new RevBlob(idBuffer); - obj.flags = SEEN; + visitationPolicy.visited(obj); objects.add(obj); return obj; } if (!(obj instanceof RevBlob)) throw new IncorrectObjectTypeException(obj, OBJ_BLOB); - obj.flags = flags = obj.flags | SEEN; - if ((flags & UNINTERESTING) == 0) + visitationPolicy.visited(obj); + if ((obj.flags & UNINTERESTING) == 0) return obj; if (boundary) return obj; @@ -383,17 +457,17 @@ public class ObjectWalk extends RevWalk { case TYPE_TREE: if (obj == null) { obj = new RevTree(idBuffer); - obj.flags = SEEN; + visitationPolicy.visited(obj); objects.add(obj); - return enterTree(obj); + return pushTree(obj); } if (!(obj instanceof RevTree)) throw new IncorrectObjectTypeException(obj, OBJ_TREE); - obj.flags = flags = obj.flags | SEEN; - if ((flags & UNINTERESTING) == 0) - return enterTree(obj); + visitationPolicy.visited(obj); + if ((obj.flags & UNINTERESTING) == 0) + return pushTree(obj); if (boundary) - return enterTree(obj); + return pushTree(obj); continue; case TYPE_GITLINK: @@ -419,30 +493,23 @@ public class ObjectWalk extends RevWalk { if (o == null) { return null; } - int flags = o.flags; - if ((flags & SEEN) != 0) + if (!visitationPolicy.shouldVisit(o)) { continue; - flags |= SEEN; - o.flags = flags; - if ((flags & UNINTERESTING) == 0 | boundary) { + } + visitationPolicy.visited(o); + if ((o.flags & UNINTERESTING) == 0 || boundary) { if (o instanceof RevTree) { - tv = newTreeVisit(o); - tv.parent = null; - currVisit = tv; + // The previous while loop should have exhausted the stack + // of trees. + assert currVisit == null; + + pushTree(o); } return o; } } } - private RevObject enterTree(RevObject obj) throws MissingObjectException, - IncorrectObjectTypeException, IOException { - TreeVisit tv = newTreeVisit(obj); - tv.parent = currVisit; - currVisit = tv; - return obj; - } - private static int findObjectId(byte[] buf, int ptr) { // Skip over the mode and name until the NUL before the ObjectId // can be located. Skip the NUL as the function returns. @@ -582,6 +649,17 @@ public class ObjectWalk extends RevWalk { } /** + * @return the current traversal depth from the root tree object + * @since 5.4 + */ + public int getTreeDepth() { + if (currVisit == null) { + return 0; + } + return currVisit.depth; + } + + /** * Get the current object's path hash code. * <p> * This method computes a hash code on the fly for this path, the hash is @@ -768,7 +846,7 @@ public class ObjectWalk extends RevWalk { } } - private TreeVisit newTreeVisit(RevObject obj) throws LargeObjectException, + private RevObject pushTree(RevObject obj) throws LargeObjectException, MissingObjectException, IncorrectObjectTypeException, IOException { TreeVisit tv = freeVisit; if (tv != null) { @@ -782,7 +860,15 @@ public class ObjectWalk extends RevWalk { } tv.obj = obj; tv.buf = reader.open(obj, OBJ_TREE).getCachedBytes(); - return tv; + tv.parent = currVisit; + currVisit = tv; + if (tv.parent == null) { + tv.depth = 1; + } else { + tv.depth = tv.parent.depth + 1; + } + + return obj; } private void releaseTreeVisit(TreeVisit tv) { @@ -812,5 +898,8 @@ public class ObjectWalk extends RevWalk { /** Number of bytes in the path leading up to this tree. */ int pathLen; + + /** Number of levels deep from the root tree. 0 for root tree. */ + int depth; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java new file mode 100644 index 0000000000..bba3c5cff3 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java @@ -0,0 +1,96 @@ +/* + * 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.revwalk; + +import java.io.IOException; +import java.util.Collection; +import java.util.Optional; + +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; + +/** + * Checks the reachability walking the graph from the starters towards the + * target. + */ +class PedestrianReachabilityChecker implements ReachabilityChecker { + + private final boolean topoSort; + + private final RevWalk walk; + + /** + * New instance of the reachability checker using a existing walk. + * + * @param topoSort + * walk commits in topological order + * @param walk + * RevWalk instance to reuse. Caller retains ownership. + */ + public PedestrianReachabilityChecker(boolean topoSort, + RevWalk walk) { + this.topoSort = topoSort; + this.walk = walk; + } + + @Override + public Optional<RevCommit> areAllReachable(Collection<RevCommit> targets, + Collection<RevCommit> starters) + throws MissingObjectException, IncorrectObjectTypeException, + IOException { + walk.reset(); + if (topoSort) { + walk.sort(RevSort.TOPO); + } + + for (RevCommit target: targets) { + walk.markStart(target); + } + + for (RevCommit starter : starters) { + walk.markUninteresting(starter); + } + + return Optional.ofNullable(walk.next()); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java new file mode 100644 index 0000000000..2ed06d1769 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java @@ -0,0 +1,90 @@ +/* + * 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.revwalk; + +import java.io.IOException; +import java.util.Collection; +import java.util.Optional; + +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; + +/** + * Check if a commit is reachable from a collection of starting commits. + * <p> + * Note that this checks the reachability of commits (and tags). Trees, blobs or + * any other object will cause IncorrectObjectTypeException exceptions. + * + * @since 5.4 + */ +public interface ReachabilityChecker { + + /** + * Check if all targets are reachable from the {@code starter} commits. + * <p> + * Caller should parse the objectIds (preferably with + * {@code walk.parseCommit()} and handle missing/incorrect type objects + * before calling this method. + * + * @param targets + * commits to reach. + * @param starters + * known starting points. + * @return An unreachable target if at least one of the targets is + * unreachable. An empty optional if all targets are reachable from + * the starters. + * + * @throws MissingObjectException + * if any of the incoming objects doesn't exist in the + * repository. + * @throws IncorrectObjectTypeException + * if any of the incoming objects is not a commit or a tag. + * @throws IOException + * if any of the underlying indexes or readers can not be + * opened. + */ + Optional<RevCommit> areAllReachable(Collection<RevCommit> targets, + Collection<RevCommit> starters) + throws MissingObjectException, IncorrectObjectTypeException, + IOException; +} 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 0a43e8fb1a..f50d189ce5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java @@ -72,6 +72,7 @@ import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter; +import org.eclipse.jgit.util.References; /** * Walks a commit graph and produces the matching commits in order. @@ -250,6 +251,23 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { } /** + * Get a reachability checker for commits over this revwalk. + * + * @return the most efficient reachability checker for this repository. + * @throws IOException + * if it cannot open any of the underlying indices. + * + * @since 5.4 + */ + public ReachabilityChecker createReachabilityChecker() throws IOException { + if (reader.getBitmapIndex() != null) { + return new BitmappedReachabilityChecker(this); + } + + return new PedestrianReachabilityChecker(true, this); + } + + /** * {@inheritDoc} * <p> * Release any resources used by this walker's reader. @@ -416,9 +434,11 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { markStart(tip); markStart(base); RevCommit mergeBase; - while ((mergeBase = next()) != null) - if (mergeBase == base) + while ((mergeBase = next()) != null) { + if (References.isSameObject(mergeBase, base)) { return true; + } + } return false; } finally { filter = oldRF; @@ -506,7 +526,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { if (sorting.size() > 1) sorting.remove(RevSort.NONE); - else if (sorting.size() == 0) + else if (sorting.isEmpty()) sorting.add(RevSort.NONE); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java index f1252a4c4b..2b721b8877 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java @@ -50,6 +50,9 @@ import java.util.List; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; /** @@ -130,7 +133,7 @@ public final class RevWalkUtils { /** * Find the list of branches a given commit is reachable from when following - * parent.s + * parents. * <p> * Note that this method calls * {@link org.eclipse.jgit.revwalk.RevWalk#reset()} at the beginning. @@ -153,15 +156,51 @@ public final class RevWalkUtils { RevWalk revWalk, Collection<Ref> refs) throws MissingObjectException, IncorrectObjectTypeException, IOException { + return findBranchesReachableFrom(commit, revWalk, refs, + NullProgressMonitor.INSTANCE); + } + + /** + * Find the list of branches a given commit is reachable from when following + * parents. + * <p> + * Note that this method calls + * {@link org.eclipse.jgit.revwalk.RevWalk#reset()} at the beginning. + * <p> + * In order to improve performance this method assumes clock skew among + * committers is never larger than 24 hours. + * + * @param commit + * the commit we are looking at + * @param revWalk + * The RevWalk to be used. + * @param refs + * the set of branches we want to see reachability from + * @param monitor + * the callback for progress and cancellation + * @return the list of branches a given commit is reachable from + * @throws org.eclipse.jgit.errors.MissingObjectException + * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException + * @throws java.io.IOException + * @since 5.4 + */ + public static List<Ref> findBranchesReachableFrom(RevCommit commit, + RevWalk revWalk, Collection<Ref> refs, ProgressMonitor monitor) + throws MissingObjectException, IncorrectObjectTypeException, + IOException { // Make sure commit is from the same RevWalk commit = revWalk.parseCommit(commit.getId()); revWalk.reset(); List<Ref> result = new ArrayList<>(); - + monitor.beginTask(JGitText.get().searchForReachableBranches, + refs.size()); final int SKEW = 24*3600; // one day clock skew for (Ref ref : refs) { + if (monitor.isCancelled()) + return result; + monitor.update(1); RevObject maybehead = revWalk.parseAny(ref.getObjectId()); if (!(maybehead instanceof RevCommit)) continue; @@ -176,6 +215,7 @@ public final class RevWalkUtils { if (revWalk.isMergedInto(commit, headCommit)) result.add(ref); } + monitor.endTask(); return result; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/RevFlagFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/RevFlagFilter.java index c67c44b216..c3dc3dea66 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/RevFlagFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/RevFlagFilter.java @@ -44,6 +44,7 @@ package org.eclipse.jgit.revwalk.filter; import java.io.IOException; +import java.util.Arrays; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; @@ -78,8 +79,7 @@ public abstract class RevFlagFilter extends RevFilter { */ public static RevFilter hasAll(RevFlag... a) { final RevFlagSet set = new RevFlagSet(); - for (RevFlag flag : a) - set.add(flag); + set.addAll(Arrays.asList(a)); return new HasAll(set); } @@ -103,8 +103,7 @@ public abstract class RevFlagFilter extends RevFilter { */ public static RevFilter hasAny(RevFlag... a) { final RevFlagSet set = new RevFlagSet(); - for (RevFlag flag : a) - set.add(flag); + set.addAll(Arrays.asList(a)); return new HasAny(set); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java index bdbd7c9072..5bb8153a58 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java @@ -148,7 +148,8 @@ public class FileBasedConfig extends StoredConfig { */ @Override public void load() throws IOException, ConfigInvalidException { - final int maxStaleRetries = 5; + final int maxRetries = 5; + int retryDelayMillis = 20; int retries = 0; while (true) { final FileSnapshot oldSnapshot = snapshot; @@ -179,6 +180,22 @@ public class FileBasedConfig extends StoredConfig { } return; } catch (FileNotFoundException noFile) { + // might be locked by another process (see exception Javadoc) + if (retries < maxRetries && configFile.exists()) { + if (LOG.isDebugEnabled()) { + LOG.debug(MessageFormat.format( + JGitText.get().configHandleMayBeLocked, + Integer.valueOf(retries)), noFile); + } + try { + Thread.sleep(retryDelayMillis); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + retries++; + retryDelayMillis *= 2; // max wait 1260 ms + continue; + } if (configFile.exists()) { throw noFile; } @@ -187,7 +204,7 @@ public class FileBasedConfig extends StoredConfig { return; } catch (IOException e) { if (FileUtils.isStaleFileHandle(e) - && retries < maxStaleRetries) { + && retries < maxRetries) { if (LOG.isDebugEnabled()) { LOG.debug(MessageFormat.format( JGitText.get().configHandleIsStale, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java index 68878e5a61..e6e3d4fb12 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java @@ -266,6 +266,10 @@ public class PackStatistics { /** Time in ms spent writing the pack. */ public long timeWriting; + /** Number of trees traversed in the walk when writing the pack. + * @since 5.4*/ + public long treesTraversed; + /** * Statistics about each object type in the pack (commits, tags, trees * and blobs.) @@ -586,6 +590,14 @@ public class PackStatistics { } /** + * @return number of trees traversed in the walk when writing the pack. + * @since 5.4 + */ + public long getTreesTraversed() { + return statistics.treesTraversed; + } + + /** * Get total time spent processing this pack. * * @return total time spent processing this pack. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java index ce8995ad8f..e5559dea09 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java @@ -144,7 +144,8 @@ public class SubmoduleWalk implements AutoCloseable { * path and .gitmodules at the root. * @param path * a {@link java.lang.String} object. - * @return generator at given path, null if no submodule at given path + * @return generator at given path. The caller is responsible for calling + * {@link #close()}. Null if no submodule at given path. * @throws java.io.IOException */ public static SubmoduleWalk forPath(Repository repository, @@ -177,7 +178,8 @@ public class SubmoduleWalk implements AutoCloseable { * path and .gitmodules at the root. * @param path * a {@link java.lang.String} object. - * @return generator at given path, null if no submodule at given path + * @return generator at given path. The caller is responsible for calling + * {@link #close()}. Null if no submodule at given path. * @throws java.io.IOException */ public static SubmoduleWalk forPath(Repository repository, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java index fcf78ac7b9..e8724b72db 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java @@ -53,6 +53,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.MessageFormat; +import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Set; @@ -182,10 +183,7 @@ abstract class BasePackConnection extends BaseConnection { } catch (TransportException err) { close(); throw err; - } catch (IOException err) { - close(); - throw new TransportException(err.getMessage(), err); - } catch (RuntimeException err) { + } catch (IOException | RuntimeException err) { close(); throw new TransportException(err.getMessage(), err); } @@ -203,7 +201,7 @@ abstract class BasePackConnection extends BaseConnection { throw noRepository(); throw eof; } - if (line == PacketLineIn.END) + if (PacketLineIn.isEnd(line)) break; if (line.startsWith("ERR ")) { //$NON-NLS-1$ @@ -217,8 +215,8 @@ abstract class BasePackConnection extends BaseConnection { if (nul >= 0) { // The first line (if any) may contain "hidden" // capability values after a NUL byte. - for (String c : line.substring(nul + 1).split(" ")) //$NON-NLS-1$ - remoteCapablities.add(c); + remoteCapablities.addAll( + Arrays.asList(line.substring(nul + 1).split(" "))); //$NON-NLS-1$ line = line.substring(0, nul); } } 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 ed7465c82a..57d6bc2466 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java @@ -245,8 +245,12 @@ public abstract class BasePackFetchConnection extends BasePackConnection private PacketLineOut pckState; - /** If not -1, the maximum blob size to be sent to the server. */ - private final long filterBlobLimit; + /** + * Either FilterSpec.NO_FILTER for a filter that doesn't filter + * anything, or a filter that indicates what and what not to send to the + * server. + */ + private final FilterSpec filterSpec; /** * Create a new connection to fetch using the native git transport. @@ -268,10 +272,11 @@ public abstract class BasePackFetchConnection extends BasePackConnection includeTags = transport.getTagOpt() != TagOpt.NO_TAGS; thinPack = transport.isFetchThin(); - filterBlobLimit = transport.getFilterBlobLimit(); + filterSpec = transport.getFilterSpec(); if (local != null) { walk = new RevWalk(local); + walk.setRetainBody(false); reachableCommits = new RevCommitList<>(); REACHABLE = walk.newFlag("REACHABLE"); //$NON-NLS-1$ COMMON = walk.newFlag("COMMON"); //$NON-NLS-1$ @@ -395,10 +400,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection } catch (CancelledException ce) { close(); return; // Caller should test (or just know) this themselves. - } catch (IOException err) { - close(); - throw new TransportException(err.getMessage(), err); - } catch (RuntimeException err) { + } catch (IOException | RuntimeException err) { close(); throw new TransportException(err.getMessage(), err); } @@ -520,10 +522,8 @@ public abstract class BasePackFetchConnection extends BasePackConnection if (first) { return false; } - if (filterBlobLimit == 0) { - p.writeString(OPTION_FILTER + " blob:none"); //$NON-NLS-1$ - } else if (filterBlobLimit > 0) { - p.writeString(OPTION_FILTER + " blob:limit=" + filterBlobLimit); //$NON-NLS-1$ + if (!filterSpec.isNoOp()) { + p.writeString(filterSpec.filterLine()); } p.end(); outNeedsEnd = false; @@ -565,7 +565,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection OPTION_MULTI_ACK_DETAILED)); } - if (filterBlobLimit >= 0 && !wantCapability(line, OPTION_FILTER)) { + if (!filterSpec.isNoOp() && !wantCapability(line, OPTION_FILTER)) { throw new PackProtocolException(uri, JGitText.get().filterRequiresCapability); } @@ -670,14 +670,14 @@ public abstract class BasePackFetchConnection extends BasePackConnection } } - if (noDone & receivedReady) { + if (noDone && receivedReady) { break SEND_HAVES; } if (statelessRPC) { state.writeTo(out, null); } - if (receivedContinue && havesSinceLastContinue > MAX_HAVES + if ((receivedContinue && havesSinceLastContinue > MAX_HAVES) || havesSent >= maxHaves) { // Our history must be really different from the remote's. // We just sent a whole slew of have lines, and it did not diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java index 847e901980..35ea35ecb8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java @@ -383,8 +383,7 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen JGitText.get().errorOccurredDuringUnpackingOnTheRemoteEnd, unpackStatus)); } - String refLine; - while ((refLine = pckIn.readString()) != PacketLineIn.END) { + for (String refLine : pckIn.readStrings()) { boolean ok = false; int refNameEnd = -1; if (refLine.startsWith("ok ")) { //$NON-NLS-1$ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java index 6f17620d91..e402de0158 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java @@ -314,6 +314,7 @@ public abstract class BaseReceivePack { protected BaseReceivePack(Repository into) { db = into; walk = new RevWalk(db); + walk.setRetainBody(false); TransferConfig tc = db.getConfig().get(TransferConfig.KEY); objectChecker = tc.newReceiveObjectChecker(); @@ -1240,7 +1241,7 @@ public abstract class BaseReceivePack { adv.advertiseCapability(CAPABILITY_PUSH_OPTIONS); } adv.advertiseCapability(OPTION_AGENT, UserAgent.get()); - adv.send(getAdvertisedOrDefaultRefs()); + adv.send(getAdvertisedOrDefaultRefs().values()); for (ObjectId obj : advertisedHaves) adv.advertiseHave(obj); if (adv.isEmpty()) @@ -1281,7 +1282,7 @@ public abstract class BaseReceivePack { return; throw eof; } - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { break; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java index 84a0972723..6cf75031cd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java @@ -112,10 +112,7 @@ class BundleFetchConnection extends BaseFetchConnection { } catch (TransportException err) { close(); throw err; - } catch (IOException err) { - close(); - throw new TransportException(transport.uri, err.getMessage(), err); - } catch (RuntimeException err) { + } catch (IOException | RuntimeException err) { close(); throw new TransportException(transport.uri, err.getMessage(), err); } @@ -205,10 +202,7 @@ class BundleFetchConnection extends BaseFetchConnection { packLock = parser.parse(NullProgressMonitor.INSTANCE); ins.flush(); } - } catch (IOException err) { - close(); - throw new TransportException(transport.uri, err.getMessage(), err); - } catch (RuntimeException err) { + } catch (IOException | RuntimeException err) { close(); throw new TransportException(transport.uri, err.getMessage(), err); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java index 56aaede80d..d1db51eca6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java @@ -227,10 +227,11 @@ public class BundleWriter { exc.add(r.getId()); packWriter.setIndexDisabled(true); packWriter.setDeltaBaseAsOffset(true); - packWriter.setThin(exc.size() > 0); + packWriter.setThin(!exc.isEmpty()); packWriter.setReuseValidatingObjects(false); - if (exc.size() == 0) + if (exc.isEmpty()) { packWriter.setTagTargets(tagTargets); + } packWriter.preparePack(monitor, inc, exc); final Writer w = new OutputStreamWriter(os, UTF_8); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java index 7289ce7b6c..2b27df2b84 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java @@ -115,36 +115,26 @@ public class Daemon { repositoryResolver = (RepositoryResolver<DaemonClient>) RepositoryResolver.NONE; - uploadPackFactory = new UploadPackFactory<DaemonClient>() { - @Override - public UploadPack create(DaemonClient req, Repository db) - throws ServiceNotEnabledException, - ServiceNotAuthorizedException { - UploadPack up = new UploadPack(db); - up.setTimeout(getTimeout()); - up.setPackConfig(getPackConfig()); - return up; - } + uploadPackFactory = (DaemonClient req, Repository db) -> { + UploadPack up = new UploadPack(db); + up.setTimeout(getTimeout()); + up.setPackConfig(getPackConfig()); + return up; }; - receivePackFactory = new ReceivePackFactory<DaemonClient>() { - @Override - public ReceivePack create(DaemonClient req, Repository db) - throws ServiceNotEnabledException, - ServiceNotAuthorizedException { - ReceivePack rp = new ReceivePack(db); - - InetAddress peer = req.getRemoteAddress(); - String host = peer.getCanonicalHostName(); - if (host == null) - host = peer.getHostAddress(); - String name = "anonymous"; //$NON-NLS-1$ - String email = name + "@" + host; //$NON-NLS-1$ - rp.setRefLogIdent(new PersonIdent(name, email)); - rp.setTimeout(getTimeout()); - - return rp; - } + receivePackFactory = (DaemonClient req, Repository db) -> { + ReceivePack rp = new ReceivePack(db); + + InetAddress peer = req.getRemoteAddress(); + String host = peer.getCanonicalHostName(); + if (host == null) + host = peer.getHostAddress(); + String name = "anonymous"; //$NON-NLS-1$ + String email = name + "@" + host; //$NON-NLS-1$ + rp.setRefLogIdent(new PersonIdent(name, email)); + rp.setTimeout(getTimeout()); + + return rp; }; services = new DaemonService[] { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java index 681ae125cb..e584440b3f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java @@ -200,6 +200,7 @@ class FetchProcess { .setAllowNonFastForwards(true) .setRefLogMessage("fetch", true); //$NON-NLS-1$ try (RevWalk walk = new RevWalk(transport.local)) { + walk.setRetainBody(false); if (monitor instanceof BatchingProgressMonitor) { ((BatchingProgressMonitor) monitor).setDelayStart( 250, TimeUnit.MILLISECONDS); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java index 40ba3a3ad2..4dd7d6ed8b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java @@ -62,7 +62,7 @@ abstract class FetchRequest { final Set<ObjectId> clientShallowCommits; - final long filterBlobLimit; + final FilterSpec filterSpec; final Set<String> clientCapabilities; @@ -82,8 +82,8 @@ abstract class FetchRequest { * how deep to go in the tree * @param clientShallowCommits * commits the client has without history - * @param filterBlobLimit - * to exclude blobs on certain conditions + * @param filterSpec + * the filter spec * @param clientCapabilities * capabilities sent in the request * @param deepenNotRefs @@ -96,13 +96,14 @@ abstract class FetchRequest { * agent as reported by the client in the request body */ FetchRequest(@NonNull Set<ObjectId> wantIds, int depth, - @NonNull Set<ObjectId> clientShallowCommits, long filterBlobLimit, + @NonNull Set<ObjectId> clientShallowCommits, + @NonNull FilterSpec filterSpec, @NonNull Set<String> clientCapabilities, int deepenSince, @NonNull List<String> deepenNotRefs, @Nullable String agent) { this.wantIds = requireNonNull(wantIds); this.depth = depth; this.clientShallowCommits = requireNonNull(clientShallowCommits); - this.filterBlobLimit = filterBlobLimit; + this.filterSpec = requireNonNull(filterSpec); this.clientCapabilities = requireNonNull(clientCapabilities); this.deepenSince = deepenSince; this.deepenNotRefs = requireNonNull(deepenNotRefs); @@ -137,10 +138,11 @@ abstract class FetchRequest { } /** - * @return the blob limit set in a "filter" line (-1 if not set) + * @return the filter spec given in a "filter" line */ - long getFilterBlobLimit() { - return filterBlobLimit; + @NonNull + FilterSpec getFilterSpec() { + return filterSpec; } /** @@ -191,4 +193,4 @@ abstract class FetchRequest { String getAgent() { return agent; } -}
\ No newline at end of file +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java index 05f4a8155f..231ab9f2cc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java @@ -42,6 +42,8 @@ */ package org.eclipse.jgit.transport; +import static java.util.Objects.requireNonNull; + import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -57,10 +59,11 @@ import org.eclipse.jgit.lib.ObjectId; final class FetchV0Request extends FetchRequest { FetchV0Request(@NonNull Set<ObjectId> wantIds, int depth, - @NonNull Set<ObjectId> clientShallowCommits, long filterBlobLimit, + @NonNull Set<ObjectId> clientShallowCommits, + @NonNull FilterSpec filterSpec, @NonNull Set<String> clientCapabilities, @Nullable String agent) { - super(wantIds, depth, clientShallowCommits, filterBlobLimit, - clientCapabilities, 0, Collections.emptyList(), agent); + super(wantIds, depth, clientShallowCommits, filterSpec, + clientCapabilities, 0, Collections.emptyList(), agent); } static final class Builder { @@ -71,7 +74,7 @@ final class FetchV0Request extends FetchRequest { final Set<ObjectId> clientShallowCommits = new HashSet<>(); - long filterBlobLimit = -1; + FilterSpec filterSpec = FilterSpec.NO_FILTER; final Set<String> clientCaps = new HashSet<>(); @@ -129,18 +132,18 @@ final class FetchV0Request extends FetchRequest { } /** - * @param filterBlobLim - * blob limit set in a "filter" line + * @param filter + * the filter set in a filter line * @return this builder */ - Builder setFilterBlobLimit(long filterBlobLim) { - filterBlobLimit = filterBlobLim; + Builder setFilterSpec(@NonNull FilterSpec filter) { + filterSpec = requireNonNull(filter); return this; } FetchV0Request build() { return new FetchV0Request(wantIds, depth, clientShallowCommits, - filterBlobLimit, clientCaps, agent); + filterSpec, clientCaps, agent); } } 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 ac6361cdeb..6c24269095 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java @@ -77,11 +77,12 @@ public final class FetchV2Request extends FetchRequest { @NonNull Set<ObjectId> wantIds, @NonNull Set<ObjectId> clientShallowCommits, int deepenSince, @NonNull List<String> deepenNotRefs, int depth, - long filterBlobLimit, + @NonNull FilterSpec filterSpec, boolean doneReceived, @NonNull Set<String> clientCapabilities, @Nullable String agent, @NonNull List<String> serverOptions) { - super(wantIds, depth, clientShallowCommits, filterBlobLimit, - clientCapabilities, deepenSince, deepenNotRefs, agent); + super(wantIds, depth, clientShallowCommits, filterSpec, + clientCapabilities, deepenSince, + deepenNotRefs, agent); this.peerHas = requireNonNull(peerHas); this.wantedRefs = requireNonNull(wantedRefs); this.doneReceived = doneReceived; @@ -98,9 +99,11 @@ public final class FetchV2Request extends FetchRequest { /** * @return list of references received in "want-ref" lines + * + * @since 5.4 */ @NonNull - List<String> getWantedRefs() { + public List<String> getWantedRefs() { return wantedRefs; } @@ -147,7 +150,7 @@ public final class FetchV2Request extends FetchRequest { int deepenSince; - long filterBlobLimit = -1; + FilterSpec filterSpec = FilterSpec.NO_FILTER; boolean doneReceived; @@ -266,12 +269,12 @@ public final class FetchV2Request extends FetchRequest { } /** - * @param filterBlobLim - * set in a "filter" line + * @param filter + * spec set in a "filter" line * @return this builder */ - Builder setFilterBlobLimit(long filterBlobLim) { - filterBlobLimit = filterBlobLim; + Builder setFilterSpec(@NonNull FilterSpec filter) { + filterSpec = requireNonNull(filter); return this; } @@ -320,7 +323,7 @@ public final class FetchV2Request extends FetchRequest { FetchV2Request build() { return new FetchV2Request(peerHas, wantedRefs, wantIds, clientShallowCommits, deepenSince, deepenNotRefs, - depth, filterBlobLimit, doneReceived, clientCapabilities, + depth, filterSpec, doneReceived, clientCapabilities, agent, Collections.unmodifiableList(serverOptions)); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FilterSpec.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FilterSpec.java new file mode 100644 index 0000000000..a663c9b470 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FilterSpec.java @@ -0,0 +1,192 @@ +/* + * 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.text.MessageFormat; + +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.errors.PackProtocolException; +import org.eclipse.jgit.internal.JGitText; + +/** + * Represents either a filter specified in a protocol "filter" line, or a + * placeholder to indicate no filtering. + * + * @since 5.4 + */ +public final class FilterSpec { + + private final long blobLimit; + + private final long treeDepthLimit; + + private FilterSpec(long blobLimit, long treeDepthLimit) { + this.blobLimit = blobLimit; + this.treeDepthLimit = treeDepthLimit; + } + + /** + * Process the content of "filter" line from the protocol. It has a shape + * like: + * + * <ul> + * <li>"blob:none" + * <li>"blob:limit=N", with N >= 0 + * <li>"tree:DEPTH", with DEPTH >= 0 + * </ul> + * + * @param filterLine + * the content of the "filter" line in the protocol + * @return a FilterSpec representing the given filter + * @throws PackProtocolException + * invalid filter because due to unrecognized format or + * negative/non-numeric filter. + */ + public static FilterSpec fromFilterLine(String filterLine) + throws PackProtocolException { + if (filterLine.equals("blob:none")) { //$NON-NLS-1$ + return FilterSpec.withBlobLimit(0); + } else if (filterLine.startsWith("blob:limit=")) { //$NON-NLS-1$ + long blobLimit = -1; + try { + blobLimit = Long + .parseLong(filterLine.substring("blob:limit=".length())); //$NON-NLS-1$ + } catch (NumberFormatException e) { + // Do not change blobLimit so that we throw a + // PackProtocolException later. + } + if (blobLimit >= 0) { + return FilterSpec.withBlobLimit(blobLimit); + } + } else if (filterLine.startsWith("tree:")) { //$NON-NLS-1$ + long treeDepthLimit = -1; + try { + treeDepthLimit = Long + .parseLong(filterLine.substring("tree:".length())); //$NON-NLS-1$ + } catch (NumberFormatException e) { + // Do not change blobLimit so that we throw a + // PackProtocolException later. + } + if (treeDepthLimit >= 0) { + return FilterSpec.withTreeDepthLimit(treeDepthLimit); + } + } + + // Did not match any known filter format. + throw new PackProtocolException( + MessageFormat.format(JGitText.get().invalidFilter, filterLine)); + } + + /** + * @param blobLimit + * the blob limit in a "blob:[limit]" or "blob:none" filter line + * @return a filter spec which filters blobs above a certain size + */ + static FilterSpec withBlobLimit(long blobLimit) { + if (blobLimit < 0) { + throw new IllegalArgumentException( + "blobLimit cannot be negative: " + blobLimit); //$NON-NLS-1$ + } + return new FilterSpec(blobLimit, -1); + } + + /** + * @param treeDepthLimit + * the tree depth limit in a "tree:[depth]" filter line + * @return a filter spec which filters blobs and trees beyond a certain tree + * depth + */ + static FilterSpec withTreeDepthLimit(long treeDepthLimit) { + if (treeDepthLimit < 0) { + throw new IllegalArgumentException( + "treeDepthLimit cannot be negative: " + treeDepthLimit); //$NON-NLS-1$ + } + return new FilterSpec(-1, treeDepthLimit); + } + + /** + * A placeholder that indicates no filtering. + */ + public static final FilterSpec NO_FILTER = new FilterSpec(-1, -1); + + /** + * @return -1 if this filter does not filter blobs based on size, or a + * non-negative integer representing the max size of blobs to allow + */ + public long getBlobLimit() { + return blobLimit; + } + + /** + * @return -1 if this filter does not filter blobs and trees based on depth, + * or a non-negative integer representing the max tree depth of + * blobs and trees to fetch + */ + public long getTreeDepthLimit() { + return treeDepthLimit; + } + + /** + * @return true if this filter doesn't filter out anything + */ + public boolean isNoOp() { + return blobLimit == -1 && treeDepthLimit == -1; + } + + /** + * @return the filter line which describes this spec, e.g. "filter blob:limit=42" + */ + @Nullable + public String filterLine() { + if (blobLimit == 0) { + return GitProtocolConstants.OPTION_FILTER + " blob:none"; //$NON-NLS-1$ + } + + if (blobLimit > 0) { + return GitProtocolConstants.OPTION_FILTER + " blob:limit=" + blobLimit; //$NON-NLS-1$ + } + + return null; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java index 6c26b7026a..01f6fec7e4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java @@ -45,14 +45,12 @@ package org.eclipse.jgit.transport; import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.UTF_8; -import java.io.File; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import org.eclipse.jgit.internal.storage.dfs.DfsRepository; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.PushCertificate.NonceStatus; @@ -78,9 +76,7 @@ public class HMACSHA1NonceGenerator implements NonceGenerator { SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1"); //$NON-NLS-1$ mac = Mac.getInstance("HmacSHA1"); //$NON-NLS-1$ mac.init(signingKey); - } catch (InvalidKeyException e) { - throw new IllegalStateException(e); - } catch (NoSuchAlgorithmException e) { + } catch (InvalidKeyException | NoSuchAlgorithmException e) { throw new IllegalStateException(e); } } @@ -89,19 +85,7 @@ public class HMACSHA1NonceGenerator implements NonceGenerator { @Override public synchronized String createNonce(Repository repo, long timestamp) throws IllegalStateException { - String path; - if (repo instanceof DfsRepository) { - path = ((DfsRepository) repo).getDescription().getRepositoryName(); - } else { - File directory = repo.getDirectory(); - if (directory != null) { - path = directory.getPath(); - } else { - throw new IllegalStateException(); - } - } - - String input = path + ":" + String.valueOf(timestamp); //$NON-NLS-1$ + String input = repo.getIdentifier() + ":" + String.valueOf(timestamp); //$NON-NLS-1$ byte[] rawHmac = mac.doFinal(input.getBytes(UTF_8)); return Long.toString(timestamp) + "-" + toHex(rawHmac); //$NON-NLS-1$ } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java index ce9e1b3def..be7111d904 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java @@ -88,6 +88,30 @@ public class HttpConfig { /** git config key for the "sslVerify" setting. */ public static final String SSL_VERIFY_KEY = "sslVerify"; //$NON-NLS-1$ + /** + * git config key for the "cookieFile" setting. + * + * @since 5.4 + */ + public static final String COOKIE_FILE_KEY = "cookieFile"; //$NON-NLS-1$ + + /** + * git config key for the "saveCookies" setting. + * + * @since 5.4 + */ + public static final String SAVE_COOKIES_KEY = "saveCookies"; //$NON-NLS-1$ + + /** + * Custom JGit config key which holds the maximum number of cookie files to + * keep in the cache. + * + * @since 5.4 + */ + public static final String COOKIE_FILE_CACHE_LIMIT_KEY = "cookieFileCacheLimit"; //$NON-NLS-1$ + + private static final int DEFAULT_COOKIE_FILE_CACHE_LIMIT = 10; + private static final String MAX_REDIRECT_SYSTEM_PROPERTY = "http.maxRedirects"; //$NON-NLS-1$ private static final int DEFAULT_MAX_REDIRECTS = 5; @@ -152,6 +176,12 @@ public class HttpConfig { private int maxRedirects; + private String cookieFile; + + private boolean saveCookies; + + private int cookieFileCacheLimit; + /** * Get the "http.postBuffer" setting * @@ -189,6 +219,40 @@ public class HttpConfig { } /** + * Get the "http.cookieFile" setting + * + * @return the value of the "http.cookieFile" setting + * + * @since 5.4 + */ + public String getCookieFile() { + return cookieFile; + } + + /** + * Get the "http.saveCookies" setting + * + * @return the value of the "http.saveCookies" setting + * + * @since 5.4 + */ + public boolean getSaveCookies() { + return saveCookies; + } + + /** + * Get the "http.cookieFileCacheLimit" setting (gives the maximum number of + * cookie files to keep in the LRU cache) + * + * @return the value of the "http.cookieFileCacheLimit" setting + * + * @since 5.4 + */ + public int getCookieFileCacheLimit() { + return cookieFileCacheLimit; + } + + /** * Creates a new {@link org.eclipse.jgit.transport.HttpConfig} tailored to * the given {@link org.eclipse.jgit.transport.URIish}. * @@ -234,6 +298,10 @@ public class HttpConfig { if (redirectLimit < 0) { redirectLimit = MAX_REDIRECTS; } + cookieFile = config.getString(HTTP, null, COOKIE_FILE_KEY); + saveCookies = config.getBoolean(HTTP, SAVE_COOKIES_KEY, false); + cookieFileCacheLimit = config.getInt(HTTP, COOKIE_FILE_CACHE_LIMIT_KEY, + DEFAULT_COOKIE_FILE_CACHE_LIMIT); String match = findMatch(config.getSubsections(HTTP), uri); if (match != null) { // Override with more specific items @@ -248,6 +316,13 @@ public class HttpConfig { if (newMaxRedirects >= 0) { redirectLimit = newMaxRedirects; } + String urlSpecificCookieFile = config.getString(HTTP, match, + COOKIE_FILE_KEY); + if (urlSpecificCookieFile != null) { + cookieFile = urlSpecificCookieFile; + } + saveCookies = config.getBoolean(HTTP, match, SAVE_COOKIES_KEY, + saveCookies); } postBuffer = postBufferSize; sslVerify = sslVerifyFlag; @@ -319,8 +394,9 @@ public class HttpConfig { // A longer path match is always preferred even over a user // match. If the path matches are equal, a match with user wins // over a match without user. - if (matchLength > bestMatchLength || !withUser && hasUser - && matchLength >= 0 && matchLength == bestMatchLength) { + if (matchLength > bestMatchLength + || (!withUser && hasUser && matchLength >= 0 + && matchLength == bestMatchLength)) { bestMatch = s; bestMatchLength = matchLength; withUser = hasUser; @@ -366,7 +442,7 @@ public class HttpConfig { int uLength = uriPath.length(); int mLength = matchPath.length(); if (mLength == uLength || matchPath.charAt(mLength - 1) == '/' - || mLength < uLength && uriPath.charAt(mLength) == '/') { + || (mLength < uLength && uriPath.charAt(mLength) == '/')) { return mLength; } return -1; @@ -386,7 +462,7 @@ public class HttpConfig { if (slash < 0) { slash = length; } - if (slash == i || slash == i + 1 && path.charAt(i) == '.') { + if (slash == i || (slash == i + 1 && path.charAt(i) == '.')) { // Skip /. or also double slashes } else if (slash == i + 2 && path.charAt(i) == '.' && path.charAt(i + 1) == '.') { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java index 9fb9062fbb..dc3dcbc545 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java @@ -107,16 +107,12 @@ class InternalFetchConnection<C> extends BasePackFetchConnection { try { final UploadPack rp = uploadPackFactory.create(req, remote); rp.upload(out_r, in_w, null); - } catch (ServiceNotEnabledException e) { + } catch (ServiceNotEnabledException + | ServiceNotAuthorizedException e) { // Ignored. Client cannot use this repository. - } catch (ServiceNotAuthorizedException e) { - // Ignored. Client cannot use this repository. - } catch (IOException err) { + } catch (IOException | RuntimeException err) { // Client side of the pipes should report the problem. err.printStackTrace(); - } catch (RuntimeException err) { - // Client side will notice we went away, and report. - err.printStackTrace(); } finally { try { out_r.close(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java index f05e0b8c7d..9663de09f4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java @@ -100,16 +100,14 @@ class InternalPushConnection<C> extends BasePackPushConnection { try { final ReceivePack rp = receivePackFactory.create(req, remote); rp.receive(out_r, in_w, System.err); - } catch (ServiceNotEnabledException e) { - // Ignored. Client cannot use this repository. - } catch (ServiceNotAuthorizedException e) { + } catch (ServiceNotEnabledException + | ServiceNotAuthorizedException e) { // Ignored. Client cannot use this repository. } catch (IOException e) { - // Since the InternalPushConnection - // is used in tests, we want to avoid hiding exceptions - // because they can point to programming errors on the server - // side. By rethrowing, the default handler will dump it - // to stderr. + // Since the InternalPushConnection is used in tests, we + // want to avoid hiding exceptions because they can point to + // programming errors on the server side. By rethrowing, the + // default handler will dump it to stderr. throw new UncheckedIOException(e); } finally { try { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java index c6e19d5762..d73e1939a6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java @@ -49,7 +49,9 @@ import static java.nio.charset.StandardCharsets.UTF_8; import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; import java.text.MessageFormat; +import java.util.Iterator; import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.internal.JGitText; @@ -72,14 +74,25 @@ import org.slf4j.LoggerFactory; public class PacketLineIn { private static final Logger log = LoggerFactory.getLogger(PacketLineIn.class); - /** Magic return from {@link #readString()} when a flush packet is found. */ + /** + * Magic return from {@link #readString()} when a flush packet is found. + * + * @deprecated Callers should use {@link #isEnd(String)} to check if a + * string is the end marker, or + * {@link PacketLineIn#readStrings()} to iterate over all + * strings in the input stream until the marker is reached. + */ + @Deprecated public static final String END = new StringBuilder(0).toString(); /* must not string pool */ /** * Magic return from {@link #readString()} when a delim packet is found. * * @since 5.0 + * @deprecated Callers should use {@link #isDelimiter(String)} to check if a + * string is the delimiter. */ + @Deprecated public static final String DELIM = new StringBuilder(0).toString(); /* must not string pool */ static enum AckNackResult { @@ -193,6 +206,20 @@ public class PacketLineIn { } /** + * Get an iterator to read strings from the input stream. + * + * @return an iterator that calls {@link #readString()} until {@link #END} + * is encountered. + * + * @throws IOException + * on failure to read the initial packet line. + * @since 5.4 + */ + public PacketLineInIterator readStrings() throws IOException { + return new PacketLineInIterator(this); + } + + /** * Read a single UTF-8 encoded string packet from the input stream. * <p> * Unlike {@link #readString()} a trailing LF will be retained. @@ -224,6 +251,54 @@ public class PacketLineIn { return s; } + /** + * Check if a string is the delimiter marker. + * + * @param s + * the string to check + * @return true if the given string is {@link #DELIM}, otherwise false. + * @since 5.4 + */ + @SuppressWarnings({ "ReferenceEquality", "StringEquality" }) + public static boolean isDelimiter(String s) { + return s == DELIM; + } + + /** + * Get the delimiter marker. + * <p> + * Intended for use only in tests. + * + * @return The delimiter marker. + */ + static String delimiter() { + return DELIM; + } + + /** + * Get the end marker. + * <p> + * Intended for use only in tests. + * + * @return The end marker. + */ + static String end() { + return END; + } + + /** + * Check if a string is the packet end marker. + * + * @param s + * the string to check + * @return true if the given string is {@link #END}, otherwise false. + * @since 5.4 + */ + @SuppressWarnings({ "ReferenceEquality", "StringEquality" }) + public static boolean isEnd(String s) { + return s == END; + } + void discardUntilEnd() throws IOException { for (;;) { int n = readLength(); @@ -282,4 +357,46 @@ public class PacketLineIn { public static class InputOverLimitIOException extends IOException { private static final long serialVersionUID = 1L; } + + /** + * Iterator over packet lines. + * <p> + * Calls {@link #readString()} on the {@link PacketLineIn} until + * {@link #END} is encountered. + * + * @since 5.4 + * + */ + public static class PacketLineInIterator implements Iterable<String> { + private PacketLineIn in; + + private String current; + + PacketLineInIterator(PacketLineIn in) throws IOException { + this.in = in; + current = in.readString(); + } + + @Override + public Iterator<String> iterator() { + return new Iterator<String>() { + @Override + public boolean hasNext() { + return !PacketLineIn.isEnd(current); + } + + @Override + public String next() { + String next = current; + try { + current = in.readString(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + return next; + } + }; + } + + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java index ba5d2f3c8f..28a146dde5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java @@ -63,12 +63,9 @@ import java.util.Collection; */ public interface PostReceiveHook { /** A simple no-op hook. */ - PostReceiveHook NULL = new PostReceiveHook() { - @Override - public void onPostReceive(final ReceivePack rp, - final Collection<ReceiveCommand> commands) { - // Do nothing. - } + PostReceiveHook NULL = (final ReceivePack rp, + final Collection<ReceiveCommand> commands) -> { + // Do nothing. }; /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java index 3aa3b127e5..251bfe271a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java @@ -57,11 +57,8 @@ import org.eclipse.jgit.storage.pack.PackStatistics; */ public interface PostUploadHook { /** A simple no-op hook. */ - PostUploadHook NULL = new PostUploadHook() { - @Override - public void onPostUpload(PackStatistics stats) { - // Do nothing. - } + PostUploadHook NULL = (PackStatistics stats) -> { + // Do nothing. }; /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java index 30845d3b68..b91756bd5e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java @@ -79,12 +79,9 @@ import java.util.Collection; */ public interface PreReceiveHook { /** A simple no-op hook. */ - PreReceiveHook NULL = new PreReceiveHook() { - @Override - public void onPreReceive(final ReceivePack rp, - final Collection<ReceiveCommand> commands) { - // Do nothing. - } + PreReceiveHook NULL = (final ReceivePack rp, + final Collection<ReceiveCommand> commands) -> { + // Do nothing. }; /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java index 21498d6f5c..428a45c09d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java @@ -99,7 +99,7 @@ final class ProtocolV0Parser { throw eof; } - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { break; } @@ -130,7 +130,7 @@ final class ProtocolV0Parser { } filterReceived = true; - reqBuilder.setFilterBlobLimit(ProtocolV2Parser.filterLine(arg)); + reqBuilder.setFilterSpec(FilterSpec.fromFilterLine(arg)); continue; } 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 8f4b86ee0a..caba15fc54 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java @@ -92,7 +92,7 @@ final class ProtocolV2Parser { String agentPrefix = OPTION_AGENT + '='; String line = pckIn.readString(); - while (line != PacketLineIn.DELIM && line != PacketLineIn.END) { + while (!PacketLineIn.isDelimiter(line) && !PacketLineIn.isEnd(line)) { if (line.startsWith(serverOptionPrefix)) { serverOptionConsumer .accept(line.substring(serverOptionPrefix.length())); @@ -133,40 +133,41 @@ final class ProtocolV2Parser { serverOption -> reqBuilder.addServerOption(serverOption), agent -> reqBuilder.setAgent(agent)); - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { return reqBuilder.build(); } - if (line != PacketLineIn.DELIM) { + if (!PacketLineIn.isDelimiter(line)) { throw new PackProtocolException( MessageFormat.format(JGitText.get().unexpectedPacketLine, line)); } boolean filterReceived = false; - while ((line = pckIn.readString()) != PacketLineIn.END) { - if (line.startsWith("want ")) { //$NON-NLS-1$ - reqBuilder.addWantId(ObjectId.fromString(line.substring(5))); + for (String line2 : pckIn.readStrings()) { + if (line2.startsWith("want ")) { //$NON-NLS-1$ + reqBuilder.addWantId(ObjectId.fromString(line2.substring(5))); } else if (transferConfig.isAllowRefInWant() - && line.startsWith(OPTION_WANT_REF + " ")) { //$NON-NLS-1$ - reqBuilder.addWantedRef(line.substring(OPTION_WANT_REF.length() + 1)); - } else if (line.startsWith("have ")) { //$NON-NLS-1$ - reqBuilder.addPeerHas(ObjectId.fromString(line.substring(5))); - } else if (line.equals("done")) { //$NON-NLS-1$ + && line2.startsWith(OPTION_WANT_REF + " ")) { //$NON-NLS-1$ + reqBuilder.addWantedRef( + line2.substring(OPTION_WANT_REF.length() + 1)); + } else if (line2.startsWith("have ")) { //$NON-NLS-1$ + reqBuilder.addPeerHas(ObjectId.fromString(line2.substring(5))); + } else if (line2.equals("done")) { //$NON-NLS-1$ reqBuilder.setDoneReceived(); - } else if (line.equals(OPTION_THIN_PACK)) { + } else if (line2.equals(OPTION_THIN_PACK)) { reqBuilder.addClientCapability(OPTION_THIN_PACK); - } else if (line.equals(OPTION_NO_PROGRESS)) { + } else if (line2.equals(OPTION_NO_PROGRESS)) { reqBuilder.addClientCapability(OPTION_NO_PROGRESS); - } else if (line.equals(OPTION_INCLUDE_TAG)) { + } else if (line2.equals(OPTION_INCLUDE_TAG)) { reqBuilder.addClientCapability(OPTION_INCLUDE_TAG); - } else if (line.equals(OPTION_OFS_DELTA)) { + } else if (line2.equals(OPTION_OFS_DELTA)) { reqBuilder.addClientCapability(OPTION_OFS_DELTA); - } else if (line.startsWith("shallow ")) { //$NON-NLS-1$ + } else if (line2.startsWith("shallow ")) { //$NON-NLS-1$ reqBuilder.addClientShallowCommit( - ObjectId.fromString(line.substring(8))); - } else if (line.startsWith("deepen ")) { //$NON-NLS-1$ - int parsedDepth = Integer.parseInt(line.substring(7)); + ObjectId.fromString(line2.substring(8))); + } else if (line2.startsWith("deepen ")) { //$NON-NLS-1$ + int parsedDepth = Integer.parseInt(line2.substring(7)); if (parsedDepth <= 0) { throw new PackProtocolException( MessageFormat.format(JGitText.get().invalidDepth, @@ -181,19 +182,19 @@ final class ProtocolV2Parser { JGitText.get().deepenNotWithDeepen); } reqBuilder.setDepth(parsedDepth); - } else if (line.startsWith("deepen-not ")) { //$NON-NLS-1$ - reqBuilder.addDeepenNotRef(line.substring(11)); + } else if (line2.startsWith("deepen-not ")) { //$NON-NLS-1$ + reqBuilder.addDeepenNotRef(line2.substring(11)); if (reqBuilder.getDepth() != 0) { throw new PackProtocolException( JGitText.get().deepenNotWithDeepen); } - } else if (line.equals(OPTION_DEEPEN_RELATIVE)) { + } else if (line2.equals(OPTION_DEEPEN_RELATIVE)) { reqBuilder.addClientCapability(OPTION_DEEPEN_RELATIVE); - } else if (line.startsWith("deepen-since ")) { //$NON-NLS-1$ - int ts = Integer.parseInt(line.substring(13)); + } else if (line2.startsWith("deepen-since ")) { //$NON-NLS-1$ + int ts = Integer.parseInt(line2.substring(13)); if (ts <= 0) { throw new PackProtocolException(MessageFormat - .format(JGitText.get().invalidTimestamp, line)); + .format(JGitText.get().invalidTimestamp, line2)); } if (reqBuilder.getDepth() != 0) { throw new PackProtocolException( @@ -201,17 +202,17 @@ final class ProtocolV2Parser { } reqBuilder.setDeepenSince(ts); } else if (transferConfig.isAllowFilter() - && line.startsWith(OPTION_FILTER + ' ')) { + && line2.startsWith(OPTION_FILTER + ' ')) { if (filterReceived) { throw new PackProtocolException( JGitText.get().tooManyFilters); } filterReceived = true; - reqBuilder.setFilterBlobLimit(filterLine( - line.substring(OPTION_FILTER.length() + 1))); + reqBuilder.setFilterSpec(FilterSpec.fromFilterLine( + line2.substring(OPTION_FILTER.length() + 1))); } else { throw new PackProtocolException(MessageFormat - .format(JGitText.get().unexpectedPacketLine, line)); + .format(JGitText.get().unexpectedPacketLine, line2)); } } @@ -244,67 +245,29 @@ final class ProtocolV2Parser { serverOption -> builder.addServerOption(serverOption), agent -> builder.setAgent(agent)); - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { return builder.build(); } - if (line != PacketLineIn.DELIM) { + if (!PacketLineIn.isDelimiter(line)) { throw new PackProtocolException(MessageFormat .format(JGitText.get().unexpectedPacketLine, line)); } - while ((line = pckIn.readString()) != PacketLineIn.END) { - if (line.equals("peel")) { //$NON-NLS-1$ + for (String line2 : pckIn.readStrings()) { + if (line2.equals("peel")) { //$NON-NLS-1$ builder.setPeel(true); - } else if (line.equals("symrefs")) { //$NON-NLS-1$ + } else if (line2.equals("symrefs")) { //$NON-NLS-1$ builder.setSymrefs(true); - } else if (line.startsWith("ref-prefix ")) { //$NON-NLS-1$ - prefixes.add(line.substring("ref-prefix ".length())); //$NON-NLS-1$ + } else if (line2.startsWith("ref-prefix ")) { //$NON-NLS-1$ + prefixes.add(line2.substring("ref-prefix ".length())); //$NON-NLS-1$ } else { throw new PackProtocolException(MessageFormat - .format(JGitText.get().unexpectedPacketLine, line)); + .format(JGitText.get().unexpectedPacketLine, line2)); } } return builder.setRefPrefixes(prefixes).build(); } - /* - * Process the content of "filter" line from the protocol. It has a shape - * like "blob:none" or "blob:limit=N", with limit a positive number. - * - * @param blobLine - * the content of the "filter" line in the protocol - * @return N, the limit, defaulting to 0 if "none" - * @throws PackProtocolException - * invalid filter because due to unrecognized format or - * negative/non-numeric filter. - */ - static long filterLine(String blobLine) throws PackProtocolException { - long blobLimit = -1; - - if (blobLine.equals("blob:none")) { //$NON-NLS-1$ - blobLimit = 0; - } else if (blobLine.startsWith("blob:limit=")) { //$NON-NLS-1$ - try { - blobLimit = Long - .parseLong(blobLine.substring("blob:limit=".length())); //$NON-NLS-1$ - } catch (NumberFormatException e) { - throw new PackProtocolException(MessageFormat - .format(JGitText.get().invalidFilter, blobLine)); - } - } - /* - * We must have (1) either "blob:none" or "blob:limit=" set (because we - * only support blob size limits for now), and (2) if the latter, then - * it must be nonnegative. Throw if (1) or (2) is not met. - */ - if (blobLimit < 0) { - throw new PackProtocolException( - MessageFormat.format(JGitText.get().invalidFilter, blobLine)); - } - - return blobLimit; - } - } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java index aeca63500a..14afc44eec 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java @@ -57,7 +57,6 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -193,81 +192,78 @@ public class PushCertificateStore implements AutoCloseable { * close resources. */ public Iterable<PushCertificate> getAll(String refName) { - return new Iterable<PushCertificate>() { - @Override - public Iterator<PushCertificate> iterator() { - return new Iterator<PushCertificate>() { - private final String path = pathName(refName); - private PushCertificate next; + return () -> new Iterator<PushCertificate>() { + private final String path = pathName(refName); + + private PushCertificate next; + + private RevWalk rw; + { + try { + if (reader == null) { + load(); + } + if (commit != null) { + rw = new RevWalk(reader); + rw.setTreeFilter(AndTreeFilter.create( + PathFilterGroup.create(Collections + .singleton(PathFilter.create(path))), + TreeFilter.ANY_DIFF)); + rw.setRewriteParents(false); + rw.markStart(rw.parseCommit(commit)); + } else { + rw = null; + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } - private RevWalk rw; - { + @Override + public boolean hasNext() { + try { + if (next == null) { + if (rw == null) { + return false; + } try { - if (reader == null) { - load(); - } - if (commit != null) { - rw = new RevWalk(reader); - rw.setTreeFilter(AndTreeFilter.create( - PathFilterGroup.create( - Collections.singleton(PathFilter.create(path))), - TreeFilter.ANY_DIFF)); - rw.setRewriteParents(false); - rw.markStart(rw.parseCommit(commit)); + RevCommit c = rw.next(); + if (c != null) { + try (TreeWalk tw = TreeWalk.forPath( + rw.getObjectReader(), path, + c.getTree())) { + next = read(tw); + } } else { - rw = null; + next = null; } } catch (IOException e) { throw new RuntimeException(e); } } - - @Override - public boolean hasNext() { - try { - if (next == null) { - if (rw == null) { - return false; - } - try { - RevCommit c = rw.next(); - if (c != null) { - try (TreeWalk tw = TreeWalk.forPath( - rw.getObjectReader(), path, c.getTree())) { - next = read(tw); - } - } else { - next = null; - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return next != null; - } finally { - if (next == null && rw != null) { - rw.close(); - rw = null; - } - } + return next != null; + } finally { + if (next == null && rw != null) { + rw.close(); + rw = null; } + } + } - @Override - public PushCertificate next() { - hasNext(); - PushCertificate n = next; - if (n == null) { - throw new NoSuchElementException(); - } - next = null; - return n; - } + @Override + public PushCertificate next() { + hasNext(); + PushCertificate n = next; + if (n == null) { + throw new NoSuchElementException(); + } + next = null; + return n; + } - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; + @Override + public void remove() { + throw new UnsupportedOperationException(); } }; } @@ -442,13 +438,8 @@ public class PushCertificateStore implements AutoCloseable { } private static void sortPending(List<PendingCert> pending) { - Collections.sort(pending, new Comparator<PendingCert>() { - @Override - public int compare(PendingCert a, PendingCert b) { - return Long.signum( - a.ident.getWhen().getTime() - b.ident.getWhen().getTime()); - } - }); + Collections.sort(pending, (PendingCert a, PendingCert b) -> Long.signum( + a.ident.getWhen().getTime() - b.ident.getWhen().getTime())); } private DirCache newDirCache() throws IOException { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java index d61aeb04d2..a9a995cd3f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java @@ -429,7 +429,7 @@ public class ReceiveCommand { this.newId = ObjectId.zeroId(); this.newSymref = newSymref; this.name = name; - if (AnyObjectId.equals(ObjectId.zeroId(), oldId)) { + if (AnyObjectId.isEqual(ObjectId.zeroId(), oldId)) { type = Type.CREATE; } else if (newSymref != null) { type = Type.UPDATE; @@ -468,7 +468,7 @@ public class ReceiveCommand { this.name = name; if (oldSymref == null) { type = Type.CREATE; - } else if (!AnyObjectId.equals(ObjectId.zeroId(), newId)) { + } else if (!AnyObjectId.isEqual(ObjectId.zeroId(), newId)) { type = Type.UPDATE; } else { type = Type.DELETE; @@ -750,7 +750,7 @@ public class ReceiveCommand { public void updateType(RevWalk walk) throws IOException { if (typeIsCorrect) return; - if (type == Type.UPDATE && !AnyObjectId.equals(oldId, newId)) { + if (type == Type.UPDATE && !AnyObjectId.isEqual(oldId, newId)) { RevObject o = walk.parseAny(oldId); RevObject n = walk.parseAny(newId); if (!(o instanceof RevCommit) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java index 4652c3fda8..d6adf1e0d8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java @@ -348,7 +348,7 @@ public class ReceivePack extends BaseReceivePack { pushOptions = new ArrayList<>(4); for (;;) { String option = in.readString(); - if (option == PacketLineIn.END) { + if (PacketLineIn.isEnd(option)) { break; } pushOptions.add(option); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java index d6d6198f5b..d19c652c25 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java @@ -61,12 +61,7 @@ public interface RefFilter { /** * The default filter, allows all refs to be shown. */ - RefFilter DEFAULT = new RefFilter() { - @Override - public Map<String, Ref> filter (Map<String, Ref> refs) { - return refs; - } - }; + RefFilter DEFAULT = (Map<String, Ref> refs) -> refs; /** * Filters a {@code Map} of refs before it is advertised to the client. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java index afd3adaacc..16c6faf277 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java @@ -49,6 +49,7 @@ import java.text.MessageFormat; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.util.References; /** * Describes how refs in one repository copy into another repository. @@ -585,8 +586,9 @@ public class RefSpec implements Serializable { } private static boolean eq(String a, String b) { - if (a == b) + if (References.isSameObject(a, b)) { return true; + } if (a == null || b == null) return false; return a.equals(b); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java index 005a0c2d0e..fdb19df91a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java @@ -100,13 +100,9 @@ public abstract class SshSessionFactory { * @since 5.2 */ public static String getLocalUserName() { - return AccessController.doPrivileged(new PrivilegedAction<String>() { - @Override - public String run() { - return SystemReader.getInstance() - .getProperty(Constants.OS_USER_NAME_KEY); - } - }); + return AccessController + .doPrivileged((PrivilegedAction<String>) () -> SystemReader + .getInstance().getProperty(Constants.OS_USER_NAME_KEY)); } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java index ba2a673a18..550a9ef372 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java @@ -175,7 +175,7 @@ public class TrackingRefUpdate { private RefUpdate.Result decode(ReceiveCommand.Result status) { switch (status) { case OK: - if (AnyObjectId.equals(oldObjectId, newObjectId)) + if (AnyObjectId.isEqual(oldObjectId, newObjectId)) return RefUpdate.Result.NO_CHANGE; switch (getType()) { case CREATE: diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java index 621c2ea56c..0b7907035e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java @@ -47,6 +47,7 @@ package org.eclipse.jgit.transport; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; import java.io.BufferedReader; import java.io.IOException; @@ -70,6 +71,7 @@ import java.util.Map; import java.util.Vector; import java.util.concurrent.CopyOnWriteArrayList; +import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.api.errors.AbortedByHookException; import org.eclipse.jgit.errors.NotSupportedException; import org.eclipse.jgit.errors.TransportException; @@ -178,10 +180,7 @@ public abstract class Transport implements AutoCloseable { TransportProtocol proto; try { proto = (TransportProtocol) f.get(null); - } catch (IllegalArgumentException e) { - // If we cannot access the field, don't. - continue; - } catch (IllegalAccessException e) { + } catch (IllegalArgumentException | IllegalAccessException e) { // If we cannot access the field, don't. continue; } @@ -790,7 +789,7 @@ public abstract class Transport implements AutoCloseable { /** Should refs no longer on the source be pruned from the destination? */ private boolean removeDeletedRefs; - private long filterBlobLimit = -1; + private FilterSpec filterSpec = FilterSpec.NO_FILTER; /** Timeout in seconds to wait before aborting an IO read or write. */ private int timeout; @@ -1067,20 +1066,42 @@ public abstract class Transport implements AutoCloseable { } /** - * @return the last value passed to {@link #setFilterBlobLimit}, or -1 if - * it was never invoked. + * @return the blob limit value set with {@link #setFilterBlobLimit} or + * {@link #setFilterSpec(FilterSpec)}, or -1 if no blob limit value + * was set * @since 5.0 + * @deprecated Use {@link #getFilterSpec()} instead */ - public long getFilterBlobLimit() { - return filterBlobLimit; + @Deprecated + public final long getFilterBlobLimit() { + return filterSpec.getBlobLimit(); } /** * @param bytes exclude blobs of size greater than this * @since 5.0 + * @deprecated Use {@link #setFilterSpec(FilterSpec)} instead + */ + @Deprecated + public final void setFilterBlobLimit(long bytes) { + setFilterSpec(FilterSpec.withBlobLimit(bytes)); + } + + /** + * @return the last filter spec set with {@link #setFilterSpec(FilterSpec)}, + * or {@link FilterSpec#NO_FILTER} if it was never invoked. + * @since 5.4 + */ + public final FilterSpec getFilterSpec() { + return filterSpec; + } + + /** + * @param filter a new filter to use for this transport + * @since 5.4 */ - public void setFilterBlobLimit(long bytes) { - filterBlobLimit = bytes; + public final void setFilterSpec(@NonNull FilterSpec filter) { + filterSpec = requireNonNull(filter); } /** 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 0df1b70cb7..a591dbae2a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java @@ -54,8 +54,11 @@ import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT; import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING; import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING; import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE; +import static org.eclipse.jgit.util.HttpSupport.HDR_COOKIE; import static org.eclipse.jgit.util.HttpSupport.HDR_LOCATION; import static org.eclipse.jgit.util.HttpSupport.HDR_PRAGMA; +import static org.eclipse.jgit.util.HttpSupport.HDR_SET_COOKIE; +import static org.eclipse.jgit.util.HttpSupport.HDR_SET_COOKIE2; import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT; import static org.eclipse.jgit.util.HttpSupport.HDR_WWW_AUTHENTICATE; import static org.eclipse.jgit.util.HttpSupport.METHOD_GET; @@ -68,11 +71,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.net.HttpCookie; import java.net.MalformedURLException; import java.net.Proxy; import java.net.ProxySelector; import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.security.cert.CertPathBuilderException; import java.security.cert.CertPathValidatorException; import java.security.cert.CertificateException; @@ -84,6 +91,8 @@ import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -100,6 +109,8 @@ import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.file.RefDirectory; +import org.eclipse.jgit.internal.transport.http.NetscapeCookieFile; +import org.eclipse.jgit.internal.transport.http.NetscapeCookieFileCache; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; @@ -114,6 +125,7 @@ import org.eclipse.jgit.transport.http.HttpConnection; import org.eclipse.jgit.util.HttpSupport; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.RawParseUtils; +import org.eclipse.jgit.util.StringUtils; import org.eclipse.jgit.util.SystemReader; import org.eclipse.jgit.util.TemporaryBuffer; import org.eclipse.jgit.util.io.DisabledOutputStream; @@ -272,6 +284,19 @@ public class TransportHttp extends HttpTransport implements WalkTransport, private boolean sslFailure = false; + /** + * All stored cookies bound to this repo (independent of the baseUrl) + */ + private final NetscapeCookieFile cookieFile; + + /** + * The cookies to be sent with each request to the given {@link #baseUrl}. + * Filtered view on top of {@link #cookieFile} where only cookies which + * apply to the current url are left. This set needs to be filtered for + * expired entries each time prior to sending them. + */ + private final Set<HttpCookie> relevantCookies; + TransportHttp(Repository local, URIish uri) throws NotSupportedException { super(local, uri); @@ -279,6 +304,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport, http = new HttpConfig(local.getConfig(), uri); proxySelector = ProxySelector.getDefault(); sslVerify = http.isSslVerify(); + cookieFile = getCookieFileFromConfig(http); + relevantCookies = filterCookies(cookieFile, baseUrl); } private URL toURL(URIish urish) throws MalformedURLException { @@ -319,6 +346,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport, http = new HttpConfig(uri); proxySelector = ProxySelector.getDefault(); sslVerify = http.isSslVerify(); + cookieFile = getCookieFileFromConfig(http); + relevantCookies = filterCookies(cookieFile, baseUrl); } /** @@ -361,9 +390,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport, try (InputStream in = openInputStream(c)) { return getConnection(c, in, service); } - } catch (NotSupportedException err) { - throw err; - } catch (TransportException err) { + } catch (NotSupportedException | TransportException err) { throw err; } catch (IOException err) { throw new TransportException(uri, JGitText.get().errorReadingInfoRefs, err); @@ -447,9 +474,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport, throw new NotSupportedException(msg); } } - } catch (NotSupportedException err) { - throw err; - } catch (TransportException err) { + } catch (NotSupportedException | TransportException err) { throw err; } catch (IOException err) { throw new TransportException(uri, JGitText.get().errorReadingInfoRefs, err); @@ -510,6 +535,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport, conn.setRequestProperty(HDR_ACCEPT, "*/*"); //$NON-NLS-1$ } final int status = HttpSupport.response(conn); + processResponseCookies(conn); switch (status) { case HttpConnection.HTTP_OK: // Check if HttpConnection did some authentication in the @@ -573,9 +599,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport, String err = status + " " + conn.getResponseMessage(); //$NON-NLS-1$ throw new TransportException(uri, err); } - } catch (NotSupportedException e) { - throw e; - } catch (TransportException e) { + } catch (NotSupportedException | TransportException e) { throw e; } catch (SSLHandshakeException e) { handleSslFailure(e); @@ -600,6 +624,57 @@ public class TransportHttp extends HttpTransport implements WalkTransport, } } + void processResponseCookies(HttpConnection conn) { + if (cookieFile != null && http.getSaveCookies()) { + List<HttpCookie> foundCookies = new LinkedList<>(); + + List<String> cookieHeaderValues = conn + .getHeaderFields(HDR_SET_COOKIE); + if (!cookieHeaderValues.isEmpty()) { + foundCookies.addAll( + extractCookies(HDR_SET_COOKIE, cookieHeaderValues)); + } + cookieHeaderValues = conn.getHeaderFields(HDR_SET_COOKIE2); + if (!cookieHeaderValues.isEmpty()) { + foundCookies.addAll( + extractCookies(HDR_SET_COOKIE2, cookieHeaderValues)); + } + if (!foundCookies.isEmpty()) { + try { + // update cookie lists with the newly received cookies! + Set<HttpCookie> cookies = cookieFile.getCookies(false); + cookies.addAll(foundCookies); + cookieFile.write(baseUrl); + relevantCookies.addAll(foundCookies); + } catch (IOException | IllegalArgumentException + | InterruptedException e) { + LOG.warn(MessageFormat.format( + JGitText.get().couldNotPersistCookies, + cookieFile.getPath()), e); + } + } + } + } + + private List<HttpCookie> extractCookies(String headerKey, + List<String> headerValues) { + List<HttpCookie> foundCookies = new LinkedList<>(); + for (String headerValue : headerValues) { + foundCookies + .addAll(HttpCookie.parse(headerKey + ':' + headerValue)); + } + // HttpCookies.parse(...) is only compliant with RFC 2965. Make it RFC + // 6265 compliant by applying the logic from + // https://tools.ietf.org/html/rfc6265#section-5.2.3 + for (HttpCookie foundCookie : foundCookies) { + String domain = foundCookie.getDomain(); + if (domain != null && domain.startsWith(".")) { //$NON-NLS-1$ + foundCookie.setDomain(domain.substring(1)); + } + } + return foundCookies; + } + private static class CredentialItems { CredentialItem.InformationalMessage message; @@ -775,7 +850,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport, } // git allows only rewriting the root, i.e., everything before INFO_REFS // or the service name - if (next.indexOf(checkFor) < 0) { + if (!next.contains(checkFor)) { return false; } // Basically we should test here that whatever follows INFO_REFS is @@ -849,14 +924,35 @@ public class TransportHttp extends HttpTransport implements WalkTransport, conn.setConnectTimeout(effTimeOut); conn.setReadTimeout(effTimeOut); } + // set cookie header if necessary + if (!relevantCookies.isEmpty()) { + setCookieHeader(conn); + } + if (this.headers != null && !this.headers.isEmpty()) { - for (Map.Entry<String, String> entry : this.headers.entrySet()) + for (Map.Entry<String, String> entry : this.headers.entrySet()) { conn.setRequestProperty(entry.getKey(), entry.getValue()); + } } authMethod.configureRequest(conn); return conn; } + private void setCookieHeader(HttpConnection conn) { + StringBuilder cookieHeaderValue = new StringBuilder(); + for (HttpCookie cookie : relevantCookies) { + if (!cookie.hasExpired()) { + if (cookieHeaderValue.length() > 0) { + cookieHeaderValue.append(';'); + } + cookieHeaderValue.append(cookie.toString()); + } + } + if (cookieHeaderValue.length() > 0) { + conn.setRequestProperty(HDR_COOKIE, cookieHeaderValue.toString()); + } + } + final InputStream openInputStream(HttpConnection conn) throws IOException { InputStream input = conn.getInputStream(); @@ -870,6 +966,150 @@ public class TransportHttp extends HttpTransport implements WalkTransport, return new TransportException(uri, why); } + private static NetscapeCookieFile getCookieFileFromConfig( + HttpConfig config) { + if (!StringUtils.isEmptyOrNull(config.getCookieFile())) { + try { + Path cookieFilePath = Paths.get(config.getCookieFile()); + return NetscapeCookieFileCache.getInstance(config) + .getEntry(cookieFilePath); + } catch (InvalidPathException e) { + LOG.warn(MessageFormat.format( + JGitText.get().couldNotReadCookieFile, + config.getCookieFile()), e); + } + } + return null; + } + + private static Set<HttpCookie> filterCookies(NetscapeCookieFile cookieFile, + URL url) { + if (cookieFile != null) { + return filterCookies(cookieFile.getCookies(true), url); + } + return Collections.emptySet(); + } + + /** + * + * @param allCookies + * a list of cookies. + * @param url + * the url for which to filter the list of cookies. + * @return only the cookies from {@code allCookies} which are relevant (i.e. + * are not expired, have a matching domain, have a matching path and + * have a matching secure attribute) + */ + private static Set<HttpCookie> filterCookies(Set<HttpCookie> allCookies, + URL url) { + Set<HttpCookie> filteredCookies = new HashSet<>(); + for (HttpCookie cookie : allCookies) { + if (cookie.hasExpired()) { + continue; + } + if (!matchesCookieDomain(url.getHost(), cookie.getDomain())) { + continue; + } + if (!matchesCookiePath(url.getPath(), cookie.getPath())) { + continue; + } + if (cookie.getSecure() && !"https".equals(url.getProtocol())) { //$NON-NLS-1$ + continue; + } + filteredCookies.add(cookie); + } + return filteredCookies; + } + + /** + * + * The utility method to check whether a host name is in a cookie's domain + * or not. Similar to {@link HttpCookie#domainMatches(String, String)} but + * implements domain matching rules according to + * <a href="https://tools.ietf.org/html/rfc6265#section-5.1.3">RFC 6265, + * section 5.1.3</a> instead of the rules from + * <a href="https://tools.ietf.org/html/rfc2965#section-3.3">RFC 2965, + * section 3.3.1</a>. + * <p> + * The former rules are also used by libcurl internally. + * <p> + * The rules are as follows + * + * A string matches another domain string if at least one of the following + * conditions holds: + * <ul> + * <li>The domain string and the string are identical. (Note that both the + * domain string and the string will have been canonicalized to lower case + * at this point.)</li> + * <li>All of the following conditions hold + * <ul> + * <li>The domain string is a suffix of the string.</li> + * <li>The last character of the string that is not included in the domain + * string is a %x2E (".") character.</li> + * <li>The string is a host name (i.e., not an IP address).</li> + * </ul> + * </li> + * </ul> + * + * @param host + * the host to compare against the cookieDomain + * @param cookieDomain + * the domain to compare against + * @return {@code true} if they domain-match; {@code false} if not + * + * @see <a href= "https://tools.ietf.org/html/rfc6265#section-5.1.3">RFC + * 6265, section 5.1.3 (Domain Matching)</a> + * @see <a href= + * "https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8206092">JDK-8206092 + * : HttpCookie.domainMatches() does not match to sub-sub-domain</a> + */ + static boolean matchesCookieDomain(String host, String cookieDomain) { + cookieDomain = cookieDomain.toLowerCase(Locale.ROOT); + host = host.toLowerCase(Locale.ROOT); + if (host.equals(cookieDomain)) { + return true; + } else { + if (!host.endsWith(cookieDomain)) { + return false; + } + return host + .charAt(host.length() - cookieDomain.length() - 1) == '.'; + } + } + + /** + * The utility method to check whether a path is matching a cookie path + * domain or not. The rules are defined by + * <a href="https://tools.ietf.org/html/rfc6265#section-5.1.4">RFC 6265, + * section 5.1.4</a>: + * + * A request-path path-matches a given cookie-path if at least one of the + * following conditions holds: + * <ul> + * <li>The cookie-path and the request-path are identical.</li> + * <li>The cookie-path is a prefix of the request-path, and the last + * character of the cookie-path is %x2F ("/").</li> + * <li>The cookie-path is a prefix of the request-path, and the first + * character of the request-path that is not included in the cookie- path is + * a %x2F ("/") character.</li> + * </ul> + * @param path + * the path to check + * @param cookiePath + * the cookie's path + * + * @return {@code true} if they path-match; {@code false} if not + */ + static boolean matchesCookiePath(String path, String cookiePath) { + if (cookiePath.equals(path)) { + return true; + } + if (!cookiePath.endsWith("/")) { //$NON-NLS-1$ + cookiePath += "/"; //$NON-NLS-1$ + } + return path.startsWith(cookiePath); + } + private boolean isSmartHttp(HttpConnection c, String service) { final String expType = "application/x-" + service + "-advertisement"; //$NON-NLS-1$ //$NON-NLS-2$ final String actType = c.getContentType(); @@ -904,7 +1144,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport, JGitText.get().expectedGot, exp, act)); } - while (pckIn.readString() != PacketLineIn.END) { + while (!PacketLineIn.isEnd(pckIn.readString())) { // for now, ignore the remaining header lines } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java index fbb2c44100..f2a2b0a1c2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java @@ -188,12 +188,8 @@ class TransportLocal extends Transport implements PackTransport { && !"git upload-pack".equals(up)) //$NON-NLS-1$ return new ForkLocalFetchConnection(); - UploadPackFactory<Void> upf = new UploadPackFactory<Void>() { - @Override - public UploadPack create(Void req, Repository db) { - return createUploadPack(db); - } - }; + UploadPackFactory<Void> upf = (Void req, + Repository db) -> createUploadPack(db); return new InternalFetchConnection<>(this, upf, null, openRepo()); } @@ -205,12 +201,8 @@ class TransportLocal extends Transport implements PackTransport { && !"git receive-pack".equals(rp)) //$NON-NLS-1$ return new ForkLocalPushConnection(); - ReceivePackFactory<Void> rpf = new ReceivePackFactory<Void>() { - @Override - public ReceivePack create(Void req, Repository db) { - return createReceivePack(db); - } - }; + ReceivePackFactory<Void> rpf = (Void req, + Repository db) -> createReceivePack(db); return new InternalPushConnection<>(this, rpf, null, openRepo()); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java index 70fb1f0e56..7ca9cc134c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java @@ -62,6 +62,7 @@ import java.util.regex.Pattern; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.util.RawParseUtils; +import org.eclipse.jgit.util.References; import org.eclipse.jgit.util.StringUtils; /** @@ -362,8 +363,8 @@ public class URIish implements Serializable { if (p.length() >= 3 && p.charAt(0) == '/' && p.charAt(2) == ':' - && (p.charAt(1) >= 'A' && p.charAt(1) <= 'Z' || p.charAt(1) >= 'a' - && p.charAt(1) <= 'z')) + && ((p.charAt(1) >= 'A' && p.charAt(1) <= 'Z') + || (p.charAt(1) >= 'a' && p.charAt(1) <= 'z'))) return p.substring(1); else if (s != null && p.length() >= 2 && p.charAt(0) == '/' && p.charAt(1) == '~') @@ -624,8 +625,9 @@ public class URIish implements Serializable { } private static boolean eq(String a, String b) { - if (a == b) + if (References.isSameObject(a, b)) { return true; + } if (StringUtils.isEmptyOrNull(a) && StringUtils.isEmptyOrNull(b)) return true; if (a == null || b == null) 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 1d0f836619..9278f42adf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -44,13 +44,11 @@ package org.eclipse.jgit.transport; import static java.util.Collections.unmodifiableMap; -import static java.util.function.Function.identity; -import static java.util.stream.Collectors.toMap; import static org.eclipse.jgit.lib.Constants.R_TAGS; import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REF_IN_WANT; +import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SERVER_OPTION; import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_FETCH; import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS; -import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SERVER_OPTION; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT; @@ -66,6 +64,7 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW; 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; +import static org.eclipse.jgit.util.RefMap.toRefMap; import java.io.ByteArrayOutputStream; import java.io.EOFException; @@ -81,8 +80,10 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.TreeMap; +import java.util.stream.Collectors; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.Nullable; @@ -107,6 +108,7 @@ import org.eclipse.jgit.revwalk.AsyncRevObjectQueue; import org.eclipse.jgit.revwalk.BitmapWalker; import org.eclipse.jgit.revwalk.DepthWalk; import org.eclipse.jgit.revwalk.ObjectWalk; +import org.eclipse.jgit.revwalk.ReachabilityChecker; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevFlag; import org.eclipse.jgit.revwalk.RevFlagSet; @@ -817,7 +819,7 @@ public class UploadPack { // Fall back to all refs. setAdvertisedRefs( db.getRefDatabase().getRefs().stream() - .collect(toMap(Ref::getName, identity()))); + .collect(toRefMap((a, b) -> b))); } return refs; } @@ -836,7 +838,7 @@ public class UploadPack { String[] prefixes = refPrefixes.toArray(new String[0]); Map<String, Ref> rs = db.getRefDatabase().getRefsByPrefix(prefixes).stream() - .collect(toMap(Ref::getName, identity(), (a, b) -> b)); + .collect(toRefMap((a, b) -> b)); if (refFilter != RefFilter.DEFAULT) { return refFilter.filter(rs); } @@ -848,7 +850,7 @@ public class UploadPack { return refs.values().stream() .filter(ref -> refPrefixes.stream() .anyMatch(ref.getName()::startsWith)) - .collect(toMap(Ref::getName, identity())); + .collect(toRefMap((a, b) -> b)); } /** @@ -871,7 +873,7 @@ public class UploadPack { names.stream() .map(refs::get) .filter(Objects::nonNull) - .collect(toMap(Ref::getName, identity(), (a, b) -> b))); + .collect(toRefMap((a, b) -> b))); } /** @@ -1066,7 +1068,7 @@ public class UploadPack { findSymrefs(adv, refsToSend); } - adv.send(refsToSend); + adv.send(refsToSend.values()); adv.end(); } @@ -1227,7 +1229,7 @@ public class UploadPack { /* EOF when awaiting command is fine */ return true; } - if (command == PacketLineIn.END) { + if (PacketLineIn.isEnd(command)) { // A blank request is valid according // to the protocol; do nothing in this // case. @@ -1477,7 +1479,8 @@ public class UploadPack { } adv.setDerefTags(true); findSymrefs(adv, advertisedOrDefaultRefs); - advertised = adv.send(advertisedOrDefaultRefs); + advertised = adv.send(advertisedOrDefaultRefs.values()); + if (adv.isEmpty()) adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$ adv.end(); @@ -1526,17 +1529,30 @@ public class UploadPack { } /** - * Returns the filter blob limit for the current request. Valid only after - * calling recvWants(). A limit -1 means no limit. + * Deprecated synonym for {@code getFilterSpec().getBlobLimit()}. * * @return filter blob limit requested by the client, or -1 if no limit * @since 5.3 + * @deprecated Use {@link #getFilterSpec()} instead */ - public long getFilterBlobLimit() { + @Deprecated + public final long getFilterBlobLimit() { + return getFilterSpec().getBlobLimit(); + } + + /** + * Returns the filter spec for the current request. Valid only after + * calling recvWants(). This may be a no-op filter spec, but it won't be + * null. + * + * @return filter requested by the client + * @since 5.4 + */ + public final FilterSpec getFilterSpec() { if (currentRequest == null) { throw new RequestNotYetReadException(); } - return currentRequest.getFilterBlobLimit(); + return currentRequest.getFilterSpec(); } /** @@ -1584,7 +1600,7 @@ public class UploadPack { throw eof; } - if (line == PacketLineIn.END) { + if (PacketLineIn.isEnd(line)) { last = processHaveLines(peerHas, last, pckOut); if (commonBase.isEmpty() || multiAck != MultiAck.OFF) pckOut.writeString("NAK\n"); //$NON-NLS-1$ @@ -1854,58 +1870,88 @@ public class UploadPack { private static void checkNotAdvertisedWants(UploadPack up, List<ObjectId> notAdvertisedWants, Set<ObjectId> reachableFrom) - throws MissingObjectException, IncorrectObjectTypeException, IOException { - // Walk the requested commits back to the provided set of commits. If any - // commit exists, a branch was deleted or rewound and the repository owner - // no longer exports that requested item. If the requested commit is merged - // into an advertised branch it will be marked UNINTERESTING and no commits - // return. + throws IOException { ObjectReader reader = up.getRevWalk().getObjectReader(); try (RevWalk walk = new RevWalk(reader)) { - AsyncRevObjectQueue q = walk.parseAny(notAdvertisedWants, true); - try { - RevObject obj; - while ((obj = q.next()) != null) { - if (!(obj instanceof RevCommit)) { - // If unadvertized non-commits are requested, use - // bitmaps. If there are no bitmaps, instead of - // incurring the expense of a manual walk, reject - // the request. - BitmapIndex bitmapIndex = reader.getBitmapIndex(); - if (bitmapIndex != null) { - checkNotAdvertisedWantsUsingBitmap( - reader, - bitmapIndex, - notAdvertisedWants, - reachableFrom); - return; - } - throw new WantNotValidException(obj); - } - walk.markStart((RevCommit) obj); + walk.setRetainBody(false); + // Missing "wants" throw exception here + List<RevObject> wantsAsObjs = objectIdsToRevObjects(walk, + notAdvertisedWants); + List<RevCommit> wantsAsCommits = wantsAsObjs.stream() + .filter(obj -> obj instanceof RevCommit) + .map(obj -> (RevCommit) obj) + .collect(Collectors.toList()); + boolean allWantsAreCommits = wantsAsObjs.size() == wantsAsCommits + .size(); + boolean repoHasBitmaps = reader.getBitmapIndex() != null; + + if (!allWantsAreCommits) { + if (!repoHasBitmaps) { + // If unadvertized non-commits are requested, use + // bitmaps. If there are no bitmaps, instead of + // incurring the expense of a manual walk, reject + // the request. + RevObject nonCommit = wantsAsObjs + .stream() + .filter(obj -> !(obj instanceof RevCommit)) + .limit(1) + .collect(Collectors.toList()).get(0); + throw new WantNotValidException(nonCommit); + } - } catch (MissingObjectException notFound) { - throw new WantNotValidException(notFound.getObjectId(), - notFound); - } finally { - q.release(); + checkNotAdvertisedWantsUsingBitmap(reader, + reader.getBitmapIndex(), notAdvertisedWants, + reachableFrom); + return; } - for (ObjectId id : reachableFrom) { - try { - walk.markUninteresting(walk.parseCommit(id)); - } catch (IncorrectObjectTypeException notCommit) { - continue; - } + + // All wants are commits, we can use ReachabilityChecker + ReachabilityChecker reachabilityChecker = walk + .createReachabilityChecker(); + + List<RevCommit> starters = objectIdsToRevCommits(walk, + reachableFrom); + Optional<RevCommit> unreachable = reachabilityChecker + .areAllReachable(wantsAsCommits, starters); + if (unreachable.isPresent()) { + throw new WantNotValidException(unreachable.get()); } - RevCommit bad = walk.next(); - if (bad != null) { - throw new WantNotValidException(bad); + } catch (MissingObjectException notFound) { + throw new WantNotValidException(notFound.getObjectId(), notFound); + } + } + + // Resolve the ObjectIds into RevObjects. Any missing object raises an + // exception + private static List<RevObject> objectIdsToRevObjects(RevWalk walk, + Iterable<ObjectId> objectIds) + throws MissingObjectException, IOException { + List<RevObject> result = new ArrayList<>(); + for (ObjectId objectId : objectIds) { + result.add(walk.parseAny(objectId)); + } + return result; + } + + // Get commits from object ids. If the id is not a commit, ignore it. If the + // id doesn't exist, report the missing object in a exception. + private static List<RevCommit> objectIdsToRevCommits(RevWalk walk, + Iterable<ObjectId> objectIds) + throws MissingObjectException, IOException { + List<RevCommit> result = new ArrayList<>(); + for (ObjectId objectId : objectIds) { + try { + result.add(walk.parseCommit(objectId)); + } catch (IncorrectObjectTypeException e) { + continue; } } + return result; } + private void addCommonBase(RevObject o) { if (!o.has(COMMON)) { o.add(COMMON); @@ -1988,21 +2034,12 @@ public class UploadPack { } catch (ServiceMayNotContinueException noPack) { // This was already reported on (below). throw noPack; - } catch (IOException err) { - if (reportInternalServerErrorOverSideband()) - throw new UploadPackInternalServerErrorException(err); - else - throw err; - } catch (RuntimeException err) { - if (reportInternalServerErrorOverSideband()) - throw new UploadPackInternalServerErrorException(err); - else - throw err; - } catch (Error err) { - if (reportInternalServerErrorOverSideband()) + } catch (IOException | RuntimeException | Error err) { + if (reportInternalServerErrorOverSideband()) { throw new UploadPackInternalServerErrorException(err); - else + } else { throw err; + } } } else { sendPack(false, req, accumulator, allTags, unshallowCommits, deepenNots); @@ -2097,15 +2134,16 @@ public class UploadPack { accumulator); try { pw.setIndexDisabled(true); - if (req.getFilterBlobLimit() >= 0) { - pw.setFilterBlobLimit(req.getFilterBlobLimit()); - pw.setUseCachedPacks(false); - } else { + if (req.getFilterSpec().isNoOp()) { pw.setUseCachedPacks(true); + } else { + pw.setFilterSpec(req.getFilterSpec()); + pw.setUseCachedPacks(false); } pw.setUseBitmaps( req.getDepth() == 0 - && req.getClientShallowCommits().isEmpty()); + && req.getClientShallowCommits().isEmpty() + && req.getFilterSpec().getTreeDepthLimit() == -1); pw.setClientShallowCommits(req.getClientShallowCommits()); pw.setReuseDeltaCommits(true); pw.setDeltaBaseAsOffset( diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java index 2bb58144ba..b289e42177 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java @@ -657,7 +657,7 @@ class WalkFetchConnection extends BaseFetchConnection { } ObjectId act = inserter.insert(type, raw); - if (!AnyObjectId.equals(id, act)) { + if (!AnyObjectId.isEqual(id, act)) { throw new TransportException(MessageFormat.format( JGitText.get().incorrectHashFor, id.name(), act.name(), Constants.typeString(type), diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java index 4c754252a3..d9103f8b27 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java @@ -165,7 +165,7 @@ class WalkPushConnection extends BaseConnection implements PushConnection { continue; } - if (AnyObjectId.equals(ObjectId.zeroId(), u.getNewObjectId())) + if (AnyObjectId.isEqual(ObjectId.zeroId(), u.getNewObjectId())) deleteCommand(u); else updates.add(u); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java index 60acd2f8ae..b4a7af094d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java @@ -127,11 +127,7 @@ public class FileResolver<C> implements RepositoryResolver<C> { } else throw new ServiceNotEnabledException(); - } catch (RuntimeException e) { - db.close(); - throw new RepositoryNotFoundException(name, e); - - } catch (IOException e) { + } catch (RuntimeException | IOException e) { db.close(); throw new RepositoryNotFoundException(name, e); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java index b850d1ef94..5d36e58c01 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java @@ -57,12 +57,8 @@ public interface ReceivePackFactory<C> { /** * A factory disabling the ReceivePack service for all repositories */ - ReceivePackFactory<?> DISABLED = new ReceivePackFactory<Object>() { - @Override - public ReceivePack create(Object req, Repository db) - throws ServiceNotEnabledException { - throw new ServiceNotEnabledException(); - } + ReceivePackFactory<?> DISABLED = (Object req, Repository db) -> { + throw new ServiceNotEnabledException(); }; /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java index 4816f21bcc..dd24b7a0f2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java @@ -57,12 +57,8 @@ public interface RepositoryResolver<C> { /** * Resolver configured to open nothing. */ - RepositoryResolver<?> NONE = new RepositoryResolver<Object>() { - @Override - public Repository open(Object req, String name) - throws RepositoryNotFoundException { - throw new RepositoryNotFoundException(name); - } + RepositoryResolver<?> NONE = (Object req, String name) -> { + throw new RepositoryNotFoundException(name); }; /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java index bb43b136d8..b8673f5eb5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java @@ -57,12 +57,8 @@ public interface UploadPackFactory<C> { /** * A factory disabling the UploadPack service for all repositories. */ - UploadPackFactory<?> DISABLED = new UploadPackFactory<Object>() { - @Override - public UploadPack create(Object req, Repository db) - throws ServiceNotEnabledException { - throw new ServiceNotEnabledException(); - } + UploadPackFactory<?> DISABLED = (Object req, Repository db) -> { + throw new ServiceNotEnabledException(); }; /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java index 61b130f93f..2f8af78f82 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java @@ -323,8 +323,7 @@ public class NameConflictTreeWalk extends TreeWalk { @Override void popEntriesEqual() throws CorruptObjectException { final AbstractTreeIterator ch = currentHead; - for (int i = 0; i < trees.length; i++) { - final AbstractTreeIterator t = trees[i]; + for (AbstractTreeIterator t : trees) { if (t.matches == ch) { if (t.matchShift == 0) t.next(1); @@ -343,8 +342,7 @@ public class NameConflictTreeWalk extends TreeWalk { @Override void skipEntriesEqual() throws CorruptObjectException { final AbstractTreeIterator ch = currentHead; - for (int i = 0; i < trees.length; i++) { - final AbstractTreeIterator t = trees[i]; + for (AbstractTreeIterator t : trees) { if (t.matches == ch) { if (t.matchShift == 0) t.skip(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java index 69303d6ee3..65d8512179 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java @@ -1338,8 +1338,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { void popEntriesEqual() throws CorruptObjectException { final AbstractTreeIterator ch = currentHead; - for (int i = 0; i < trees.length; i++) { - final AbstractTreeIterator t = trees[i]; + for (AbstractTreeIterator t : trees) { if (t.matches == ch) { t.next(1); t.matches = null; @@ -1349,8 +1348,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { void skipEntriesEqual() throws CorruptObjectException { final AbstractTreeIterator ch = currentHead; - for (int i = 0; i < trees.length; i++) { - final AbstractTreeIterator t = trees[i]; + for (AbstractTreeIterator t : trees) { if (t.matches == ch) { t.skip(); t.matches = null; @@ -1398,10 +1396,8 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { * @param <T> * a tree type. */ - public <T extends AbstractTreeIterator> T getTree( - Class<T> type) { - for (int i = 0; i < trees.length; i++) { - AbstractTreeIterator tree = trees[i]; + public <T extends AbstractTreeIterator> T getTree(Class<T> type) { + for (AbstractTreeIterator tree : trees) { if (type.isInstance(tree)) { return type.cast(tree); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index e8ff1a947a..8bb68dca17 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -790,14 +790,10 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { return attributesNode; } - private static final Comparator<Entry> ENTRY_CMP = new Comparator<Entry>() { - @Override - public int compare(Entry a, Entry b) { - return Paths.compare( - a.encodedName, 0, a.encodedNameLen, a.getMode().getBits(), - b.encodedName, 0, b.encodedNameLen, b.getMode().getBits()); - } - }; + private static final Comparator<Entry> ENTRY_CMP = (Entry a, + Entry b) -> Paths.compare(a.encodedName, 0, a.encodedNameLen, + a.getMode().getBits(), b.encodedName, 0, b.encodedNameLen, + b.getMode().getBits()); /** * Constructor helper. @@ -1543,6 +1539,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { ObjectId blobId = entry.getObjectId(); if (entry.getStage() > 0 && entry.getStage() != DirCacheEntry.STAGE_2) { + blobId = null; // Merge conflict: check ours (stage 2) byte[] name = entry.getRawPath(); int i = 0; @@ -1550,7 +1547,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { dirCache.next(1); i++; entry = dirCache.getDirCacheEntry(); - if (!Arrays.equals(name, entry.getRawPath())) { + if (entry == null + || !Arrays.equals(name, entry.getRawPath())) { break; } if (entry.getStage() == DirCacheEntry.STAGE_2) { @@ -1560,17 +1558,20 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } dirCache.back(i); } - try (ObjectReader reader = repository.newObjectReader()) { - ObjectLoader loader = reader.open(blobId, Constants.OBJ_BLOB); - try { - return RawText.isCrLfText(loader.getCachedBytes()); - } catch (LargeObjectException e) { - try (InputStream in = loader.openStream()) { - return RawText.isCrLfText(in); + if (blobId != null) { + try (ObjectReader reader = repository.newObjectReader()) { + ObjectLoader loader = reader.open(blobId, + Constants.OBJ_BLOB); + try { + return RawText.isCrLfText(loader.getCachedBytes()); + } catch (LargeObjectException e) { + try (InputStream in = loader.openStream()) { + return RawText.isCrLfText(in); + } } + } catch (IOException e) { + // Ignore and return false below } - } catch (IOException e) { - // Ignore and return false below } } return false; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java index 738ccbd8b7..c28f03534c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java @@ -113,7 +113,7 @@ public class TreeFilterMarker { try { boolean marked = filter.include(walk); if (marked) - marks |= (1L << index); + marks |= (1 << index); } catch (StopWalkException e) { // Don't check tree filter anymore, it will no longer // match 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 a9cef59636..90305013f5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -363,7 +363,7 @@ public abstract class FS { } return attributes; }); - f.exceptionally(e -> { + f = f.exceptionally(e -> { LOG.error(e.getLocalizedMessage(), e); return Optional.empty(); }); @@ -1042,13 +1042,9 @@ public abstract class FS { * @return the user's home directory; null if the user does not have one. */ protected File userHomeImpl() { - final String home = AccessController - .doPrivileged(new PrivilegedAction<String>() { - @Override - public String run() { - return System.getProperty("user.home"); //$NON-NLS-1$ - } - }); + final String home = AccessController.doPrivileged( + (PrivilegedAction<String>) () -> System.getProperty("user.home") //$NON-NLS-1$ + ); if (home == null || home.length() == 0) return null; return new File(home).getAbsoluteFile(); 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 eda0fae247..a485389a9a 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 @@ -51,8 +51,9 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.nio.charset.Charset; -import java.nio.file.AccessDeniedException; import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileStore; +import java.nio.file.FileSystemException; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; @@ -62,9 +63,11 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.errors.JGitInternalException; @@ -88,7 +91,7 @@ public class FS_POSIX extends FS { private static final int DEFAULT_UMASK = 0022; private volatile int umask = -1; - private volatile boolean supportsUnixNLink = true; + private static final Map<FileStore, Boolean> CAN_HARD_LINK = new ConcurrentHashMap<>(); private volatile AtomicFileCreation supportsAtomicFileCreation = AtomicFileCreation.UNDEFINED; @@ -375,12 +378,23 @@ public class FS_POSIX extends FS { if (!lock.createNewFile()) { return false; } - if (supportsAtomicCreateNewFile() || !supportsUnixNLink) { + if (supportsAtomicCreateNewFile()) { return true; } Path lockPath = lock.toPath(); Path link = null; + FileStore store = null; try { + store = Files.getFileStore(lockPath); + } catch (SecurityException e) { + return true; + } + try { + Boolean canLink = CAN_HARD_LINK.computeIfAbsent(store, + s -> Boolean.TRUE); + if (Boolean.FALSE.equals(canLink)) { + return true; + } link = Files.createLink( Paths.get(lock.getAbsolutePath() + ".lnk"), //$NON-NLS-1$ lockPath); @@ -392,11 +406,11 @@ public class FS_POSIX extends FS { nlink)); return false; } else if (nlink < 2) { - supportsUnixNLink = false; + CAN_HARD_LINK.put(store, Boolean.FALSE); } return true; } catch (UnsupportedOperationException | IllegalArgumentException e) { - supportsUnixNLink = false; + CAN_HARD_LINK.put(store, Boolean.FALSE); return true; } finally { if (link != null) { @@ -439,11 +453,22 @@ public class FS_POSIX extends FS { } catch (FileAlreadyExistsException | InvalidPathException e) { return token(false, null); } - if (supportsAtomicCreateNewFile() || !supportsUnixNLink) { + if (supportsAtomicCreateNewFile()) { return token(true, null); } Path link = null; + FileStore store = null; try { + store = Files.getFileStore(path); + } catch (SecurityException e) { + return token(true, null); + } + try { + Boolean canLink = CAN_HARD_LINK.computeIfAbsent(store, + s -> Boolean.TRUE); + if (Boolean.FALSE.equals(canLink)) { + return token(true, null); + } link = Files.createLink(Paths.get(uniqueLinkPath(file)), path); Integer nlink = (Integer) (Files.getAttribute(path, "unix:nlink")); //$NON-NLS-1$ @@ -452,12 +477,12 @@ public class FS_POSIX extends FS { JGitText.get().failedAtomicFileCreation, path, nlink)); return token(false, link); } else if (nlink.intValue() < 2) { - supportsUnixNLink = false; + CAN_HARD_LINK.put(store, Boolean.FALSE); } return token(true, link); } catch (UnsupportedOperationException | IllegalArgumentException - | AccessDeniedException | SecurityException e) { - supportsUnixNLink = false; + | FileSystemException | SecurityException e) { + CAN_HARD_LINK.put(store, Boolean.FALSE); return token(true, link); } } 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 7c07270363..7fe80bb21a 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 @@ -205,18 +205,21 @@ public class FS_Win32 extends FS { @Override protected File userHomeImpl() { String home = SystemReader.getInstance().getenv("HOME"); //$NON-NLS-1$ - if (home != null) + if (home != null) { return resolve(null, home); + } String homeDrive = SystemReader.getInstance().getenv("HOMEDRIVE"); //$NON-NLS-1$ if (homeDrive != null) { String homePath = SystemReader.getInstance().getenv("HOMEPATH"); //$NON-NLS-1$ - if (homePath != null) + if (homePath != null) { return new File(homeDrive, homePath); + } } String homeShare = SystemReader.getInstance().getenv("HOMESHARE"); //$NON-NLS-1$ - if (homeShare != null) + if (homeShare != null) { return new File(homeShare); + } return super.userHomeImpl(); } @@ -237,8 +240,9 @@ public class FS_Win32 extends FS { /** {@inheritDoc} */ @Override public boolean supportsSymlinks() { - if (supportSymlinks == null) + if (supportSymlinks == null) { detectSymlinkSupport(); + } return Boolean.TRUE.equals(supportSymlinks); } @@ -254,12 +258,13 @@ public class FS_Win32 extends FS { | InternalError e) { supportSymlinks = Boolean.FALSE; } finally { - if (tempFile != null) + if (tempFile != null) { try { FileUtils.delete(tempFile); } catch (IOException e) { throw new RuntimeException(e); // panic } + } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java index e5c8d9d398..9a163e8e38 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java @@ -80,12 +80,9 @@ public class FS_Win32_Cygwin extends FS_Win32 { */ public static boolean isCygwin() { final String path = AccessController - .doPrivileged(new PrivilegedAction<String>() { - @Override - public String run() { - return System.getProperty("java.library.path"); //$NON-NLS-1$ - } - }); + .doPrivileged((PrivilegedAction<String>) () -> System + .getProperty("java.library.path") //$NON-NLS-1$ + ); if (path == null) return false; File found = FS.searchPath(path, "cygpath.exe"); //$NON-NLS-1$ @@ -141,13 +138,9 @@ public class FS_Win32_Cygwin extends FS_Win32 { /** {@inheritDoc} */ @Override protected File userHomeImpl() { - final String home = AccessController - .doPrivileged(new PrivilegedAction<String>() { - @Override - public String run() { - return System.getenv("HOME"); //$NON-NLS-1$ - } - }); + final String home = AccessController.doPrivileged( + (PrivilegedAction<String>) () -> System.getenv("HOME") //$NON-NLS-1$ + ); if (home == null || home.length() == 0) return super.userHomeImpl(); return resolve(new File("."), home); //$NON-NLS-1$ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java index 9f7d9a236e..4d791e470a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java @@ -48,6 +48,7 @@ package org.eclipse.jgit.util; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.file.AtomicMoveNotSupportedException; @@ -200,31 +201,50 @@ public class FileUtils { if ((options & EMPTY_DIRECTORIES_ONLY) != 0) { if (f.isDirectory()) { delete = true; - } else { - if ((options & IGNORE_ERRORS) == 0) - throw new IOException(MessageFormat.format( - JGitText.get().deleteFileFailed, - f.getAbsolutePath())); + } else if ((options & IGNORE_ERRORS) == 0) { + throw new IOException(MessageFormat.format( + JGitText.get().deleteFileFailed, f.getAbsolutePath())); } } else { delete = true; } - if (delete && !f.delete()) { - if ((options & RETRY) != 0 && fs.exists(f)) { + if (delete) { + Throwable t = null; + Path p = f.toPath(); + try { + Files.delete(p); + return; + } catch (FileNotFoundException e) { + if ((options & (SKIP_MISSING | IGNORE_ERRORS)) == 0) { + throw new IOException(MessageFormat.format( + JGitText.get().deleteFileFailed, + f.getAbsolutePath()), e); + } + return; + } catch (IOException e) { + t = e; + } + if ((options & RETRY) != 0) { for (int i = 1; i < 10; i++) { try { Thread.sleep(100); - } catch (InterruptedException e) { + } catch (InterruptedException ex) { // ignore } - if (f.delete()) + try { + Files.deleteIfExists(p); return; + } catch (IOException e) { + t = e; + } } } - if ((options & IGNORE_ERRORS) == 0) + if ((options & IGNORE_ERRORS) == 0) { throw new IOException(MessageFormat.format( - JGitText.get().deleteFileFailed, f.getAbsolutePath())); + JGitText.get().deleteFileFailed, f.getAbsolutePath()), + t); + } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GSSManagerFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GSSManagerFactory.java index da57999e29..5927b33355 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/GSSManagerFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GSSManagerFactory.java @@ -145,13 +145,8 @@ public abstract class GSSManagerFactory { return (GSSManager) GSS_MANAGER_IMPL_CONSTRUCTOR .newInstance(httpCaller); - } catch (InstantiationException e) { - throw new Error(e); - } catch (IllegalAccessException e) { - throw new Error(e); - } catch (IllegalArgumentException e) { - throw new Error(e); - } catch (InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException e) { throw new Error(e); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java index a339b9aba0..56a173163d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java @@ -126,7 +126,7 @@ public class GitDateParser { DEFAULT("EEE MMM dd HH:mm:ss yyyy Z"), // //$NON-NLS-1$ LOCAL("EEE MMM dd HH:mm:ss yyyy"); //$NON-NLS-1$ - String formatStr; + private final String formatStr; private ParseableSimpleDateFormat(String formatStr) { this.formatStr = formatStr; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java index 9190a5915a..640670debc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java @@ -170,6 +170,27 @@ public class HttpSupport { public static final String HDR_WWW_AUTHENTICATE = "WWW-Authenticate"; //$NON-NLS-1$ /** + * The {@code Cookie} header. + * + * @since 5.4 + */ + public static final String HDR_COOKIE = "Cookie"; //$NON-NLS-1$ + + /** + * The {@code Set-Cookie} header. + * + * @since 5.4 + */ + public static final String HDR_SET_COOKIE = "Set-Cookie"; //$NON-NLS-1$ + + /** + * The {@code Set-Cookie2} header. + * + * @since 5.4 + */ + public static final String HDR_SET_COOKIE2 = "Set-Cookie2"; //$NON-NLS-1$ + + /** * URL encode a value string into an output buffer. * * @param urlstr @@ -303,9 +324,7 @@ public class HttpSupport { try { conn.configure(null, trustAllCerts, null); conn.setHostnameVerifier(new DummyHostnameVerifier()); - } catch (KeyManagementException e) { - throw new IOException(e.getMessage()); - } catch (NoSuchAlgorithmException e) { + } catch (KeyManagementException | NoSuchAlgorithmException e) { throw new IOException(e.getMessage()); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/LRUMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/LRUMap.java new file mode 100644 index 0000000000..41c15363f8 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/LRUMap.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018, Konrad Windszus <konrad_w@gmx.de> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.util; + +import java.util.LinkedHashMap; + +/** + * Map with only up to n entries. If a new entry is added so that the map + * contains more than those n entries the least-recently used entry is removed + * from the map. + * + * @param <K> + * the type of keys maintained by this map + * @param <V> + * the type of mapped values + * + * @since 5.4 + */ +public class LRUMap<K, V> extends LinkedHashMap<K, V> { + + private static final long serialVersionUID = 4329609127403759486L; + + private final int limit; + + /** + * Constructs an empty map which may contain at most the given amount of + * entries. + * + * @param initialCapacity + * the initial capacity + * @param limit + * the number of entries the map should have at most + */ + public LRUMap(int initialCapacity, int limit) { + super(initialCapacity, 0.75f, true); + this.limit = limit; + } + + @Override + protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) { + return size() > limit; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java index 639c353621..60dead51b2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java @@ -48,7 +48,10 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; +import java.util.function.BinaryOperator; +import java.util.stream.Collector; +import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefComparator; @@ -333,6 +336,32 @@ public class RefList<T extends Ref> implements Iterable<Ref> { } /** + * Create a {@link Collector} for {@link Ref}. + * + * @param mergeFunction + * if specified the result will be sorted and deduped. + * @return {@link Collector} for {@link Ref} + * @since 5.4 + */ + public static <T extends Ref> Collector<T, ?, RefList<T>> toRefList( + @Nullable BinaryOperator<T> mergeFunction) { + return Collector.of( + () -> new Builder<>(), + Builder<T>::add, (b1, b2) -> { + Builder<T> b = new Builder<>(); + b.addAll(b1); + b.addAll(b2); + return b; + }, (b) -> { + if (mergeFunction != null) { + b.sort(); + b.dedupe(mergeFunction); + } + return b.toRefList(); + }); + } + + /** * Builder to facilitate fast construction of an immutable RefList. * * @param <T> @@ -405,6 +434,16 @@ public class RefList<T extends Ref> implements Iterable<Ref> { } /** + * Add all items from another builder. + * + * @param other + * @since 5.4 + */ + public void addAll(Builder other) { + addAll(other.list, 0, other.size); + } + + /** * Add all items from a source array. * <p> * References must be added in sort order, or the array must be sorted @@ -444,6 +483,31 @@ public class RefList<T extends Ref> implements Iterable<Ref> { Arrays.sort(list, 0, size, RefComparator.INSTANCE); } + /** + * Dedupe the refs in place. Must be called after {@link #sort}. + * + * @param mergeFunction + */ + @SuppressWarnings("unchecked") + void dedupe(BinaryOperator<T> mergeFunction) { + if (size == 0) { + return; + } + int lastElement = 0; + for (int i = 1; i < size; i++) { + if (RefComparator.INSTANCE.compare(list[lastElement], + list[i]) == 0) { + list[lastElement] = mergeFunction + .apply((T) list[lastElement], (T) list[i]); + } else { + list[lastElement + 1] = list[i]; + lastElement++; + } + } + size = lastElement + 1; + Arrays.fill(list, size, list.length, null); + } + /** @return an unmodifiable list using this collection's backing array. */ public RefList<T> toRefList() { return new RefList<>(list, size); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java index a3f9730f13..9663e3cef5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java @@ -49,6 +49,9 @@ import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; +import java.util.function.BinaryOperator; +import java.util.stream.Collector; +import java.util.stream.Collectors; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.ObjectId; @@ -285,6 +288,21 @@ public class RefMap extends AbstractMap<String, Ref> { return r.toString(); } + /** + * Create a {@link Collector} for {@link Ref}. + * + * @param mergeFunction + * @return {@link Collector} for {@link Ref} + * @since 5.4 + */ + public static Collector<Ref, ?, RefMap> toRefMap( + BinaryOperator<Ref> mergeFunction) { + return Collectors.collectingAndThen(RefList.toRefList(mergeFunction), + (refs) -> new RefMap("", //$NON-NLS-1$ + refs, RefList.emptyList(), + RefList.emptyList())); + } + private String toRefName(String name) { if (0 < prefix.length()) name = prefix + name; @@ -425,8 +443,10 @@ public class RefMap extends AbstractMap<String, Ref> { if (r.getName().equals(ref.getName())) { final ObjectId a = r.getObjectId(); final ObjectId b = ref.getObjectId(); - if (a != null && b != null && AnyObjectId.equals(a, b)) + if (a != null && b != null + && AnyObjectId.isEqual(a, b)) { return true; + } } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/References.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/References.java new file mode 100644 index 0000000000..341fbfa943 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/References.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019, Matthias Sohn <matthias.sohn@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.util; + +/** + * Utility methods for object references + * + * @since 5.4 + */ +public interface References { + + /** + * Compare two references + * + * @param <T> + * type of the references + * @param ref1 + * first reference + * @param ref2 + * second reference + * @return {@code true} if both references refer to the same object + */ + @SuppressWarnings("ReferenceEquality") + public static <T> boolean isSameObject(T ref1, T ref2) { + return ref1 == ref2; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SimpleLruCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SimpleLruCache.java index 3fcfd21fc5..c213bf9686 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SimpleLruCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SimpleLruCache.java @@ -159,6 +159,7 @@ public class SimpleLruCache<K, V> { * * @return value mapped for this key, or {@code null} if no value is mapped */ + @SuppressWarnings("NonAtomicVolatileUpdate") public V get(Object key) { Entry<K, V> entry = map.get(key); if (entry != null) { @@ -185,6 +186,7 @@ public class SimpleLruCache<K, V> { * @throws NullPointerException * if the specified key or value is null */ + @SuppressWarnings("NonAtomicVolatileUpdate") public V put(@NonNull K key, @NonNull V value) { map.put(key, new Entry<>(key, value, tick())); if (map.size() > maximumSize) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java index 3868e56f50..f4b6f9d0df 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java @@ -139,8 +139,9 @@ public final class StringUtils { * @return true if a equals b */ public static boolean equalsIgnoreCase(String a, String b) { - if (a == b) + if (References.isSameObject(a, b)) { return true; + } if (a.length() != b.length()) return false; for (int i = 0; i < a.length(); i++) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java index cccac662c4..b80ffb51b5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java @@ -451,12 +451,9 @@ public abstract class SystemReader { } private String getOsName() { - return AccessController.doPrivileged(new PrivilegedAction<String>() { - @Override - public String run() { - return getProperty("os.name"); //$NON-NLS-1$ - } - }); + return AccessController.doPrivileged( + (PrivilegedAction<String>) () -> getProperty("os.name") //$NON-NLS-1$ + ); } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/IsolatedOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/IsolatedOutputStream.java index 65470d4ef1..18de7052d9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/IsolatedOutputStream.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/IsolatedOutputStream.java @@ -88,7 +88,7 @@ public class IsolatedOutputStream extends OutputStream { public IsolatedOutputStream(OutputStream out) { dst = out; copier = new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, - new ArrayBlockingQueue<Runnable>(1), new NamedThreadFactory()); + new ArrayBlockingQueue<>(1), new NamedThreadFactory()); } /** {@inheritDoc} */ @@ -102,12 +102,9 @@ public class IsolatedOutputStream extends OutputStream { public void write(byte[] buf, int pos, int cnt) throws IOException { checkClosed(); - execute(new Callable<Void>() { - @Override - public Void call() throws IOException { - dst.write(buf, pos, cnt); - return null; - } + execute(() -> { + dst.write(buf, pos, cnt); + return null; }); } @@ -115,12 +112,9 @@ public class IsolatedOutputStream extends OutputStream { @Override public void flush() throws IOException { checkClosed(); - execute(new Callable<Void>() { - @Override - public Void call() throws IOException { - dst.flush(); - return null; - } + execute(() -> { + dst.flush(); + return null; }); } @@ -159,12 +153,9 @@ public class IsolatedOutputStream extends OutputStream { } private void cleanClose() throws IOException { - execute(new Callable<Void>() { - @Override - public Void call() throws IOException { - dst.close(); - return null; - } + execute(() -> { + dst.close(); + return null; }); } @@ -178,12 +169,9 @@ public class IsolatedOutputStream extends OutputStream { Future<Void> close; try { - close = copier.submit(new Callable<Void>() { - @Override - public Void call() throws IOException { - dst.close(); - return null; - } + close = copier.submit(() -> { + dst.close(); + return null; }); } catch (RejectedExecutionException e) { throw new IOException(e); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java index caabcef574..9431aafbde 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java @@ -68,12 +68,10 @@ public class ThrowingPrintWriter extends Writer { */ public ThrowingPrintWriter(Writer out) { this.out = out; - LF = AccessController.doPrivileged(new PrivilegedAction<String>() { - @Override - public String run() { - return SystemReader.getInstance().getProperty("line.separator"); //$NON-NLS-1$ - } - }); + LF = AccessController + .doPrivileged((PrivilegedAction<String>) () -> SystemReader + .getInstance().getProperty("line.separator") //$NON-NLS-1$ + ); } /** {@inheritDoc} */ |