aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/.settings/.api_filters11
-rw-r--r--org.eclipse.jgit/BUILD5
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF227
-rw-r--r--org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF1
-rw-r--r--org.eclipse.jgit/pom.xml29
-rw-r--r--org.eclipse.jgit/resources/META-INF/services/org.eclipse.jgit.transport.SshSessionFactory1
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java166
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java24
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/errors/ServiceUnavailableException.java42
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/RawTextComparator.java28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/CharacterHead.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/AddToBitmapWithCacheFilter.java91
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java65
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java40
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java153
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BitmapCommit.java163
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java40
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/connectivity/FullConnectivityChecker.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/transport/internal/FullConnectivityChecker.java)2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/connectivity/IterativeConnectivityChecker.java152
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/DelegatingSSLSocketFactory.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/transport/internal/DelegatingSSLSocketFactory.java)2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java24
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKey.java38
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java614
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyPassphrasePrompt.java101
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java132
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapWalker.java33
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java36
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java132
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/DefaultSshSessionFactory.java36
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java530
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java372
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java345
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigStore.java114
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java16
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java30
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java19
55 files changed, 1261 insertions, 2714 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
deleted file mode 100644
index c8b7bf799e..0000000000
--- a/org.eclipse.jgit/.settings/.api_filters
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<component id="org.eclipse.jgit" version="2">
- <resource path="src/org/eclipse/jgit/transport/http/HttpConnection.java" type="org.eclipse.jgit.transport.http.HttpConnection">
- <filter id="403767336">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.transport.http.HttpConnection"/>
- <message_argument value="HTTP_11_MOVED_PERM"/>
- </message_arguments>
- </filter>
- </resource>
-</component>
diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD
index dcfeb1702a..f7970976b0 100644
--- a/org.eclipse.jgit/BUILD
+++ b/org.eclipse.jgit/BUILD
@@ -20,12 +20,7 @@ java_library(
resources = RESOURCES,
deps = [
":insecure_cipher_factory",
- "//lib:bcpg",
- "//lib:bcpkix",
- "//lib:bcprov",
"//lib:javaewah",
- "//lib:jsch",
- "//lib:jzlib",
"//lib:slf4j-api",
],
)
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 0b60c8c46b..52aa8a353f 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -6,62 +6,76 @@ Bundle-SymbolicName: org.eclipse.jgit
Bundle-Version: 6.0.0.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %Bundle-Vendor
-Bundle-ActivationPolicy: lazy
+Eclipse-ExtensibleAPI: true
Export-Package: org.eclipse.jgit.annotations;version="6.0.0",
org.eclipse.jgit.api;version="6.0.0";
- uses:="org.eclipse.jgit.revwalk,
- org.eclipse.jgit.treewalk.filter,
- org.eclipse.jgit.diff,
- org.eclipse.jgit.util,
+ uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.notes,
org.eclipse.jgit.dircache,
- org.eclipse.jgit.api.errors,
org.eclipse.jgit.lib,
+ org.eclipse.jgit.revwalk,
+ org.eclipse.jgit.treewalk.filter,
+ org.eclipse.jgit.diff,
org.eclipse.jgit.treewalk,
- org.eclipse.jgit.blame,
+ org.eclipse.jgit.util,
org.eclipse.jgit.submodule,
- org.eclipse.jgit.transport,
+ org.eclipse.jgit.api.errors,
+ org.eclipse.jgit.revwalk.filter,
+ org.eclipse.jgit.blame,
org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="6.0.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="6.0.0",
+ org.eclipse.jgit.api.errors;version="6.0.0";
+ uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="6.0.0";
+ uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.treewalk",
org.eclipse.jgit.blame;version="6.0.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff",
org.eclipse.jgit.diff;version="6.0.0";
- uses:="org.eclipse.jgit.patch,
- org.eclipse.jgit.lib,
- org.eclipse.jgit.treewalk,
+ uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.attributes,
org.eclipse.jgit.revwalk,
+ org.eclipse.jgit.patch,
org.eclipse.jgit.treewalk.filter,
+ org.eclipse.jgit.treewalk,
org.eclipse.jgit.util",
org.eclipse.jgit.dircache;version="6.0.0";
- uses:="org.eclipse.jgit.lib,
+ uses:="org.eclipse.jgit.events,
+ org.eclipse.jgit.lib,
+ org.eclipse.jgit.attributes,
org.eclipse.jgit.treewalk,
- org.eclipse.jgit.util,
- org.eclipse.jgit.events,
- org.eclipse.jgit.attributes",
+ org.eclipse.jgit.util",
org.eclipse.jgit.errors;version="6.0.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.internal.storage.pack,
- org.eclipse.jgit.transport,
- org.eclipse.jgit.dircache",
- org.eclipse.jgit.events;version="6.0.0";uses:="org.eclipse.jgit.lib",
+ uses:="org.eclipse.jgit.transport,
+ org.eclipse.jgit.dircache,
+ org.eclipse.jgit.lib,
+ org.eclipse.jgit.internal.storage.pack",
+ org.eclipse.jgit.events;version="6.0.0";
+ uses:="org.eclipse.jgit.lib",
org.eclipse.jgit.fnmatch;version="6.0.0",
org.eclipse.jgit.gitrepo;version="6.0.0";
- uses:="org.eclipse.jgit.api,
+ uses:="org.xml.sax.helpers,
+ 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="6.0.0";x-internal:=true,
org.eclipse.jgit.hooks;version="6.0.0";uses:="org.eclipse.jgit.lib",
org.eclipse.jgit.ignore;version="6.0.0",
- org.eclipse.jgit.ignore.internal;version="6.0.0";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="6.0.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.fsck;version="6.0.0";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.ketch;version="6.0.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.ignore.internal;version="6.0.0";
+ x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="6.0.0";
+ x-friends:="org.eclipse.jgit.test,
+ org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.fsck;version="6.0.0";
+ x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.ketch;version="6.0.0";
+ x-friends:="org.eclipse.jgit.junit,
+ org.eclipse.jgit.test,
+ org.eclipse.jgit.pgm",
org.eclipse.jgit.internal.revwalk;version="6.0.0";x-internal:=true,
org.eclipse.jgit.internal.storage.dfs;version="6.0.0";
x-friends:="org.eclipse.jgit.test,
@@ -77,101 +91,142 @@ Export-Package: org.eclipse.jgit.annotations;version="6.0.0",
org.eclipse.jgit.pgm,
org.eclipse.jgit.pgm.test,
org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.internal.storage.io;version="6.0.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.pack;version="6.0.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.io;version="6.0.0";
+ x-friends:="org.eclipse.jgit.junit,
+ org.eclipse.jgit.test,
+ org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.pack;version="6.0.0";
+ x-friends:="org.eclipse.jgit.junit,
+ org.eclipse.jgit.test,
+ org.eclipse.jgit.pgm",
org.eclipse.jgit.internal.storage.reftable;version="6.0.0";
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="6.0.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftree;version="6.0.0";
+ x-friends:="org.eclipse.jgit.junit,
+ org.eclipse.jgit.test,
+ org.eclipse.jgit.pgm",
org.eclipse.jgit.internal.submodule;version="6.0.0";x-internal:=true,
- org.eclipse.jgit.internal.transport.http;version="6.0.0";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.parser;version="6.0.0";x-friends:="org.eclipse.jgit.http.server,org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.ssh;version="6.0.0";x-friends:="org.eclipse.jgit.ssh.apache",
+ org.eclipse.jgit.internal.transport.connectivity;version="6.0.0";
+ x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.transport.http;version="6.0.0";
+ x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.transport.parser;version="6.0.0";
+ x-friends:="org.eclipse.jgit.http.server,
+ org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.transport.ssh;version="6.0.0";
+ x-friends:="org.eclipse.jgit.ssh.apache,
+ org.eclipse.jgit.ssh.jsch",
org.eclipse.jgit.lib;version="6.0.0";
- uses:="org.eclipse.jgit.revwalk,
- org.eclipse.jgit.treewalk.filter,
- org.eclipse.jgit.util,
- org.eclipse.jgit.events,
+ uses:="org.eclipse.jgit.transport,
+ org.eclipse.jgit.util.sha1,
org.eclipse.jgit.dircache,
+ org.eclipse.jgit.revwalk,
org.eclipse.jgit.internal.storage.file,
+ org.eclipse.jgit.attributes,
+ org.eclipse.jgit.events,
+ com.googlecode.javaewah,
+ org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.treewalk,
- org.eclipse.jgit.transport,
- org.eclipse.jgit.submodule",
- org.eclipse.jgit.lib.internal;version="6.0.0";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.util,
+ org.eclipse.jgit.submodule,
+ org.eclipse.jgit.util.time",
+ org.eclipse.jgit.lib.internal;version="6.0.0";
+ x-friends:="org.eclipse.jgit.test",
org.eclipse.jgit.merge;version="6.0.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.treewalk,
+ uses:="org.eclipse.jgit.dircache,
+ org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.diff,
- org.eclipse.jgit.dircache,
- org.eclipse.jgit.api",
+ org.eclipse.jgit.treewalk,
+ org.eclipse.jgit.util,
+ org.eclipse.jgit.api,
+ org.eclipse.jgit.attributes",
org.eclipse.jgit.nls;version="6.0.0",
org.eclipse.jgit.notes;version="6.0.0";
uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
+ org.eclipse.jgit.treewalk,
org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="6.0.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="6.0.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.patch;version="6.0.0";
+ uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="6.0.0";
+ uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.revwalk",
org.eclipse.jgit.revwalk;version="6.0.0";
uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.treewalk,
+ org.eclipse.jgit.diff,
org.eclipse.jgit.treewalk.filter,
+ org.eclipse.jgit.revwalk.filter,
+ org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.revwalk.filter;version="6.0.0";
+ uses:="org.eclipse.jgit.revwalk,
+ org.eclipse.jgit.lib,
+ org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="6.0.0";
+ uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="6.0.0";
+ uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="6.0.0";
+ uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.diff,
- org.eclipse.jgit.revwalk.filter",
- org.eclipse.jgit.revwalk.filter;version="6.0.0";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="6.0.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="6.0.0";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="6.0.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.treewalk.filter,
+ org.eclipse.jgit.treewalk,
+ org.eclipse.jgit.util",
org.eclipse.jgit.transport;version="6.0.0";
- uses:="org.eclipse.jgit.transport.resolver,
- org.eclipse.jgit.revwalk,
- org.eclipse.jgit.internal.storage.pack,
- com.jcraft.jsch,
- org.eclipse.jgit.util,
+ uses:="javax.crypto,
org.eclipse.jgit.util.io,
- org.eclipse.jgit.internal.storage.file,
- org.eclipse.jgit.internal.transport.parser,
org.eclipse.jgit.lib,
+ org.eclipse.jgit.revwalk,
org.eclipse.jgit.transport.http,
- org.eclipse.jgit.errors,
- org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="6.0.0";uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="6.0.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.internal.storage.file,
+ org.eclipse.jgit.treewalk,
+ org.eclipse.jgit.util,
+ org.eclipse.jgit.internal.storage.pack,
+ org.eclipse.jgit.transport.resolver,
+ org.eclipse.jgit.storage.pack,
+ org.eclipse.jgit.errors",
+ org.eclipse.jgit.transport.http;version="6.0.0";
+ uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="6.0.0";
+ uses:="org.eclipse.jgit.transport,
+ org.eclipse.jgit.lib",
org.eclipse.jgit.treewalk;version="6.0.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.revwalk,
+ uses:="org.eclipse.jgit.dircache,
+ org.eclipse.jgit.lib,
org.eclipse.jgit.attributes,
+ org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
- org.eclipse.jgit.util,
- org.eclipse.jgit.dircache",
- org.eclipse.jgit.treewalk.filter;version="6.0.0";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util",
+ org.eclipse.jgit.treewalk.filter;version="6.0.0";
+ uses:="org.eclipse.jgit.treewalk",
org.eclipse.jgit.util;version="6.0.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.transport.http,
+ uses:="org.eclipse.jgit.transport,
+ org.eclipse.jgit.hooks,
+ org.eclipse.jgit.revwalk,
org.eclipse.jgit.storage.file,
- org.ietf.jgss",
- org.eclipse.jgit.util.io;version="6.0.0",
+ org.ietf.jgss,
+ org.eclipse.jgit.attributes,
+ javax.management,
+ org.eclipse.jgit.lib,
+ org.eclipse.jgit.transport.http,
+ org.eclipse.jgit.treewalk,
+ javax.net.ssl,
+ org.eclipse.jgit.util.time",
+ org.eclipse.jgit.util.io;version="6.0.0";
+ uses:="org.eclipse.jgit.attributes,
+ org.eclipse.jgit.lib,
+ org.eclipse.jgit.treewalk",
org.eclipse.jgit.util.sha1;version="6.0.0",
org.eclipse.jgit.util.time;version="6.0.0"
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.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 134cbacbee..e1904d5d3f 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -5,3 +5,4 @@ Bundle-SymbolicName: org.eclipse.jgit.source
Bundle-Vendor: Eclipse.org - JGit
Bundle-Version: 6.0.0.qualifier
Eclipse-SourceBundle: org.eclipse.jgit;version="6.0.0.qualifier";roots="."
+
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index 0c1d8ea888..1ab15beced 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit/pom.xml
@@ -37,16 +37,6 @@
<dependencies>
<dependency>
- <groupId>com.jcraft</groupId>
- <artifactId>jsch</artifactId>
- </dependency>
-
- <dependency>
- <groupId>com.jcraft</groupId>
- <artifactId>jzlib</artifactId>
- </dependency>
-
- <dependency>
<groupId>com.googlecode.javaewah</groupId>
<artifactId>JavaEWAH</artifactId>
</dependency>
@@ -56,21 +46,6 @@
<artifactId>slf4j-api</artifactId>
</dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk15on</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk15on</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk15on</artifactId>
- </dependency>
-
</dependencies>
<build>
@@ -169,7 +144,7 @@
<breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
<onlyBinaryIncompatible>false</onlyBinaryIncompatible>
<includeSynthetic>false</includeSynthetic>
- <ignoreMissingClasses>false</ignoreMissingClasses>
+ <ignoreMissingClasses>true</ignoreMissingClasses>
<skipPomModules>true</skipPomModules>
</parameter>
<skip>false</skip>
@@ -235,7 +210,7 @@
<breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
<onlyBinaryIncompatible>false</onlyBinaryIncompatible>
<includeSynthetic>false</includeSynthetic>
- <ignoreMissingClasses>false</ignoreMissingClasses>
+ <ignoreMissingClasses>true</ignoreMissingClasses>
<skipPomModules>true</skipPomModules>
</parameter>
<skip>false</skip>
diff --git a/org.eclipse.jgit/resources/META-INF/services/org.eclipse.jgit.transport.SshSessionFactory b/org.eclipse.jgit/resources/META-INF/services/org.eclipse.jgit.transport.SshSessionFactory
deleted file mode 100644
index 1f8828457b..0000000000
--- a/org.eclipse.jgit/resources/META-INF/services/org.eclipse.jgit.transport.SshSessionFactory
+++ /dev/null
@@ -1 +0,0 @@
-org.eclipse.jgit.transport.DefaultSshSessionFactory
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 c9ca11b543..e6da551bb7 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -269,6 +269,7 @@ exceptionCaughtDuringExecutionOfTagCommand=Exception caught during execution of
exceptionHookExecutionInterrupted=Execution of "{0}" hook interrupted.
exceptionOccurredDuringAddingOfOptionToALogCommand=Exception occurred during adding of {0} as option to a Log command
exceptionOccurredDuringReadingOfGIT_DIR=Exception occurred during reading of $GIT_DIR/{0}. {1}
+exceptionWhileFindingUserHome=Problem determining the user home directory, trying Java user.home
exceptionWhileReadingPack=Exception caught while accessing pack file {0}, the pack file might be corrupt. Caught {1} consecutive errors while trying to read this pack.
expectedACKNAKFoundEOF=Expected ACK/NAK, found EOF
expectedACKNAKGot=Expected ACK/NAK, got: {0}
@@ -304,15 +305,6 @@ flagsAlreadyCreated={0} flags already created.
funnyRefname=funny refname
gcFailed=Garbage collection failed.
gcTooManyUnpruned=Too many loose, unpruneable objects after garbage collection. Consider adjusting gc.auto or gc.pruneExpire.
-gpgFailedToParseSecretKey=Failed to parse secret key file in directory: {0}. Is the entered passphrase correct?
-gpgNoCredentialsProvider=missing credentials provider
-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
hoursAgo={0} hours ago
httpConfigCannotNormalizeURL=Cannot normalize URL path {0}: too many .. segments
@@ -356,6 +348,7 @@ invalidGitdirRef = Invalid .git reference in file ''{0}''
invalidGitModules=Invalid .gitmodules file
invalidGitType=invalid git type: {0}
invalidHexString=Invalid hex string: {0}
+invalidHomeDirectory=Invalid home directory: {0}
invalidHooksPath=Invalid git config core.hooksPath = {0}
invalidId=Invalid id: {0}
invalidId0=Invalid id
@@ -617,6 +610,7 @@ shortReadOfBlock=Short read of block.
shortReadOfOptionalDIRCExtensionExpectedAnotherBytes=Short read of optional DIRC extension {0}; expected another {1} bytes within the section.
shortSkipOfBlock=Short skip of block.
signingNotSupportedOnTag=Signing isn't supported on tag operations yet.
+signingServiceUnavailable=Signing service is not available
similarityScoreMustBeWithinBounds=Similarity score must be between 0 and 100.
skipMustBeNonNegative=skip must be >= 0
skipNotAccessiblePath=The path ''{0}'' isn't accessible. Skip it.
@@ -627,7 +621,6 @@ sourceRefDoesntResolveToAnyObject=Source ref {0} doesn''t resolve to any object.
sourceRefNotSpecifiedForRefspec=Source ref not specified for refspec: {0}
squashCommitNotUpdatingHEAD=Squash commit -- not updating HEAD
sshCommandFailed=Execution of ssh command ''{0}'' failed with error ''{1}''
-sshUserNameError=Jsch error: failed to set SSH user name correctly to ''{0}''; using ''{1}'' picked up from SSH config file.
sslFailureExceptionMessage=Secure connection to {0} could not be established because of SSL problems
sslFailureInfo=A secure connection to {0} could not be established because the server''s certificate could not be validated.
sslFailureCause=SSL reported: {0}
@@ -688,7 +681,6 @@ transportProtoLocal=Local Git Repository
transportProtoSFTP=SFTP
transportProtoSSH=SSH
transportProtoTest=Test
-transportSSHRetryInterrupt=Interrupted while waiting for retry
treeEntryAlreadyExists=Tree entry "{0}" already exists.
treeFilterMarkerTooManyFilters=Too many markTreeFilters passed, maximum number is {0} (passed {1})
treeWalkMustHaveExactlyTwoTrees=TreeWalk should have exactly two trees.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
index 0d4b3da6a3..e228e8276a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2012, IBM Corporation and others. and others
+ * Copyright (C) 2011, 2020 IBM Corporation and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -9,18 +9,15 @@
*/
package org.eclipse.jgit.api;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import org.eclipse.jgit.api.errors.GitAPIException;
@@ -168,71 +165,156 @@ public class ApplyCommand extends GitCommand<ApplyResult> {
for (int i = 0; i < rt.size(); i++)
oldLines.add(rt.getString(i));
List<String> newLines = new ArrayList<>(oldLines);
+ int afterLastHunk = 0;
+ int lineNumberShift = 0;
+ int lastHunkNewLine = -1;
for (HunkHeader hh : fh.getHunks()) {
+ // We assume hunks to be ordered
+ if (hh.getNewStartLine() <= lastHunkNewLine) {
+ throw new PatchApplyException(MessageFormat
+ .format(JGitText.get().patchApplyException, hh));
+ }
+ lastHunkNewLine = hh.getNewStartLine();
+
byte[] b = new byte[hh.getEndOffset() - hh.getStartOffset()];
System.arraycopy(hh.getBuffer(), hh.getStartOffset(), b, 0,
b.length);
RawText hrt = new RawText(b);
List<String> hunkLines = new ArrayList<>(hrt.size());
- for (int i = 0; i < hrt.size(); i++)
+ for (int i = 0; i < hrt.size(); i++) {
hunkLines.add(hrt.getString(i));
- int pos = 0;
- for (int j = 1; j < hunkLines.size(); j++) {
+ }
+
+ if (hh.getNewStartLine() == 0) {
+ // Must be the single hunk for clearing all content
+ if (fh.getHunks().size() == 1
+ && canApplyAt(hunkLines, newLines, 0)) {
+ newLines.clear();
+ break;
+ }
+ throw new PatchApplyException(MessageFormat
+ .format(JGitText.get().patchApplyException, hh));
+ }
+ // Hunk lines as reported by the hunk may be off, so don't rely on
+ // them.
+ int applyAt = hh.getNewStartLine() - 1 + lineNumberShift;
+ // But they definitely should not go backwards.
+ if (applyAt < afterLastHunk && lineNumberShift < 0) {
+ applyAt = hh.getNewStartLine() - 1;
+ lineNumberShift = 0;
+ }
+ if (applyAt < afterLastHunk) {
+ throw new PatchApplyException(MessageFormat
+ .format(JGitText.get().patchApplyException, hh));
+ }
+ boolean applies = false;
+ int oldLinesInHunk = hh.getLinesContext()
+ + hh.getOldImage().getLinesDeleted();
+ if (oldLinesInHunk <= 1) {
+ // Don't shift hunks without context lines. Just try the
+ // position corrected by the current lineNumberShift, and if
+ // that fails, the position recorded in the hunk header.
+ applies = canApplyAt(hunkLines, newLines, applyAt);
+ if (!applies && lineNumberShift != 0) {
+ applyAt = hh.getNewStartLine() - 1;
+ applies = applyAt >= afterLastHunk
+ && canApplyAt(hunkLines, newLines, applyAt);
+ }
+ } else {
+ int maxShift = applyAt - afterLastHunk;
+ for (int shift = 0; shift <= maxShift; shift++) {
+ if (canApplyAt(hunkLines, newLines, applyAt - shift)) {
+ applies = true;
+ applyAt -= shift;
+ break;
+ }
+ }
+ if (!applies) {
+ // Try shifting the hunk downwards
+ applyAt = hh.getNewStartLine() - 1 + lineNumberShift;
+ maxShift = newLines.size() - applyAt - oldLinesInHunk;
+ for (int shift = 1; shift <= maxShift; shift++) {
+ if (canApplyAt(hunkLines, newLines, applyAt + shift)) {
+ applies = true;
+ applyAt += shift;
+ break;
+ }
+ }
+ }
+ }
+ if (!applies) {
+ throw new PatchApplyException(MessageFormat
+ .format(JGitText.get().patchApplyException, hh));
+ }
+ // Hunk applies at applyAt. Apply it, and update afterLastHunk and
+ // lineNumberShift
+ lineNumberShift = applyAt - hh.getNewStartLine() + 1;
+ int sz = hunkLines.size();
+ for (int j = 1; j < sz; j++) {
String hunkLine = hunkLines.get(j);
switch (hunkLine.charAt(0)) {
case ' ':
- if (!newLines.get(hh.getNewStartLine() - 1 + pos).equals(
- hunkLine.substring(1))) {
- throw new PatchApplyException(MessageFormat.format(
- JGitText.get().patchApplyException, hh));
- }
- pos++;
+ applyAt++;
break;
case '-':
- if (hh.getNewStartLine() == 0) {
- newLines.clear();
- } else {
- if (!newLines.get(hh.getNewStartLine() - 1 + pos)
- .equals(hunkLine.substring(1))) {
- throw new PatchApplyException(MessageFormat.format(
- JGitText.get().patchApplyException, hh));
- }
- newLines.remove(hh.getNewStartLine() - 1 + pos);
- }
+ newLines.remove(applyAt);
break;
case '+':
- newLines.add(hh.getNewStartLine() - 1 + pos,
- hunkLine.substring(1));
- pos++;
+ newLines.add(applyAt++, hunkLine.substring(1));
+ break;
+ default:
break;
}
}
+ afterLastHunk = applyAt;
}
- if (!isNoNewlineAtEndOfFile(fh))
+ if (!isNoNewlineAtEndOfFile(fh)) {
newLines.add(""); //$NON-NLS-1$
- if (!rt.isMissingNewlineAtEnd())
+ }
+ if (!rt.isMissingNewlineAtEnd()) {
oldLines.add(""); //$NON-NLS-1$
- if (!isChanged(oldLines, newLines))
- return; // don't touch the file
- StringBuilder sb = new StringBuilder();
- for (String l : newLines) {
- // don't bother handling line endings - if it was windows, the \r is
- // still there!
- sb.append(l).append('\n');
}
- if (sb.length() > 0) {
- sb.deleteCharAt(sb.length() - 1);
+ if (!isChanged(oldLines, newLines)) {
+ return; // Don't touch the file
}
- try (Writer fw = new OutputStreamWriter(new FileOutputStream(f),
- UTF_8)) {
- fw.write(sb.toString());
+ try (Writer fw = Files.newBufferedWriter(f.toPath())) {
+ for (Iterator<String> l = newLines.iterator(); l.hasNext();) {
+ fw.write(l.next());
+ if (l.hasNext()) {
+ // Don't bother handling line endings - if it was Windows,
+ // the \r is still there!
+ fw.write('\n');
+ }
+ }
}
-
getRepository().getFS().setExecute(f, fh.getNewMode() == FileMode.EXECUTABLE_FILE);
}
+ private boolean canApplyAt(List<String> hunkLines, List<String> newLines,
+ int line) {
+ int sz = hunkLines.size();
+ int limit = newLines.size();
+ int pos = line;
+ for (int j = 1; j < sz; j++) {
+ String hunkLine = hunkLines.get(j);
+ switch (hunkLine.charAt(0)) {
+ case ' ':
+ case '-':
+ if (pos >= limit
+ || !newLines.get(pos).equals(hunkLine.substring(1))) {
+ return false;
+ }
+ pos++;
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+ }
+
private static boolean isChanged(List<String> ol, List<String> nl) {
if (ol.size() != nl.size())
return true;
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 4e18b5994d..b4f7175036 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -27,6 +27,7 @@ import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.NoMessageException;
+import org.eclipse.jgit.api.errors.ServiceUnavailableException;
import org.eclipse.jgit.api.errors.UnmergedPathsException;
import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
@@ -55,7 +56,6 @@ 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.internal.BouncyCastleGpgSigner;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
@@ -140,12 +140,16 @@ public class CommitCommand extends GitCommand<RevCommit> {
* collected by the setter methods of this class. Each instance of this
* class should only be used for one invocation of the command (means: one
* call to {@link #call()})
+ *
+ * @throws ServiceUnavailableException
+ * if signing service is not available e.g. since it isn't
+ * installed
*/
@Override
- public RevCommit call() throws GitAPIException, NoHeadException,
- NoMessageException, UnmergedPathsException,
- ConcurrentRefUpdateException, WrongRepositoryStateException,
- AbortedByHookException {
+ public RevCommit call() throws GitAPIException, AbortedByHookException,
+ ConcurrentRefUpdateException, NoHeadException, NoMessageException,
+ ServiceUnavailableException, UnmergedPathsException,
+ WrongRepositoryStateException {
checkCallable();
Collections.sort(only);
@@ -239,6 +243,10 @@ public class CommitCommand extends GitCommand<RevCommit> {
commit.setTreeId(indexTreeId);
if (signCommit.booleanValue()) {
+ if (gpgSigner == null) {
+ throw new ServiceUnavailableException(
+ JGitText.get().signingServiceUnavailable);
+ }
gpgSigner.sign(commit, signingKey, committer,
credentialsProvider);
}
@@ -510,7 +518,8 @@ public class CommitCommand extends GitCommand<RevCommit> {
*
* @throws NoMessageException
* if the commit message has not been specified
- * @throws UnsupportedSigningFormatException if the configured gpg.format is not supported
+ * @throws UnsupportedSigningFormatException
+ * if the configured gpg.format is not supported
*/
private void processOptions(RepositoryState state, RevWalk rw)
throws NoMessageException, UnsupportedSigningFormatException {
@@ -581,9 +590,6 @@ public class CommitCommand extends GitCommand<RevCommit> {
JGitText.get().onlyOpenPgpSupportedForSigning);
}
gpgSigner = GpgSigner.getDefault();
- if (gpgSigner == null) {
- gpgSigner = new BouncyCastleGpgSigner();
- }
}
}
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 01306f4129..64314772b7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -18,6 +18,8 @@ import java.io.IOException;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryBuilder;
import org.eclipse.jgit.lib.RepositoryCache;
+import org.eclipse.jgit.lib.internal.WorkQueue;
+import org.eclipse.jgit.nls.NLS;
import org.eclipse.jgit.util.FS;
/**
@@ -171,6 +173,15 @@ public class Git implements AutoCloseable {
}
/**
+ * Shutdown JGit and release resources it holds like NLS and thread pools
+ * @since 5.8
+ */
+ public static void shutdown() {
+ WorkQueue.getExecutor().shutdownNow();
+ NLS.clear();
+ }
+
+ /**
* Construct a new {@link org.eclipse.jgit.api.Git} object which can
* interact with the specified git repository.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/ServiceUnavailableException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/ServiceUnavailableException.java
new file mode 100644
index 0000000000..207ded0262
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/ServiceUnavailableException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020, 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 v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.api.errors;
+
+/**
+ * Exception thrown when an optional service is not available
+ *
+ * @since 5.8
+ */
+public class ServiceUnavailableException extends GitAPIException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor for ServiceUnavailableException
+ *
+ * @param message
+ * error message
+ * @param cause
+ * a {@link java.lang.Throwable}
+ */
+ public ServiceUnavailableException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructor for ServiceUnavailableException
+ *
+ * @param message
+ * error message
+ */
+ public ServiceUnavailableException(String message) {
+ super(message);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawTextComparator.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawTextComparator.java
index 508d07c200..0c41b8598b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawTextComparator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawTextComparator.java
@@ -191,21 +191,15 @@ public abstract class RawTextComparator extends SequenceComparator<RawText> {
be = trimTrailingWhitespace(b.content, bs, be);
while (as < ae && bs < be) {
- byte ac = a.content[as];
- byte bc = b.content[bs];
+ byte ac = a.content[as++];
+ byte bc = b.content[bs++];
- if (ac != bc)
- return false;
-
- if (isWhitespace(ac))
+ if (isWhitespace(ac) && isWhitespace(bc)) {
as = trimLeadingWhitespace(a.content, as, ae);
- else
- as++;
-
- if (isWhitespace(bc))
bs = trimLeadingWhitespace(b.content, bs, be);
- else
- bs++;
+ } else if (ac != bc) {
+ return false;
+ }
}
return as == ae && bs == be;
}
@@ -215,12 +209,12 @@ public abstract class RawTextComparator extends SequenceComparator<RawText> {
int hash = 5381;
end = trimTrailingWhitespace(raw, ptr, end);
while (ptr < end) {
- byte c = raw[ptr];
- hash = ((hash << 5) + hash) + (c & 0xff);
- if (isWhitespace(c))
+ byte c = raw[ptr++];
+ if (isWhitespace(c)) {
ptr = trimLeadingWhitespace(raw, ptr, end);
- else
- ptr++;
+ c = ' ';
+ }
+ hash = ((hash << 5) + hash) + (c & 0xff);
}
return hash;
}
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 e8e1984306..8c51a7ac2f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -1217,7 +1217,7 @@ public class DirCacheCheckout {
if (e != null && !FileMode.TREE.equals(e.getFileMode()))
builder.add(e);
if (force) {
- if (f.isModified(e, true, walk.getObjectReader())) {
+ if (f == null || f.isModified(e, true, walk.getObjectReader())) {
kept.add(path);
checkoutEntry(repo, e, walk.getObjectReader(), false,
new CheckoutMetadata(walk.getEolStreamType(CHECKOUT_OP),
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/CharacterHead.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/CharacterHead.java
index ebffa19b1d..faf4ee66c9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/CharacterHead.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/CharacterHead.java
@@ -19,7 +19,7 @@ final class CharacterHead extends AbstractHead {
* @param expectedCharacter
* expected {@code char}
*/
- protected CharacterHead(char expectedCharacter) {
+ CharacterHead(char expectedCharacter) {
super(false);
this.expectedCharacter = expectedCharacter;
}
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 ec2414d41b..782a3f872d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -297,6 +297,7 @@ public class JGitText extends TranslationBundle {
/***/ public String exceptionHookExecutionInterrupted;
/***/ public String exceptionOccurredDuringAddingOfOptionToALogCommand;
/***/ public String exceptionOccurredDuringReadingOfGIT_DIR;
+ /***/ public String exceptionWhileFindingUserHome;
/***/ public String exceptionWhileReadingPack;
/***/ public String expectedACKNAKFoundEOF;
/***/ public String expectedACKNAKGot;
@@ -332,15 +333,6 @@ public class JGitText extends TranslationBundle {
/***/ public String funnyRefname;
/***/ public String gcFailed;
/***/ public String gcTooManyUnpruned;
- /***/ public String gpgFailedToParseSecretKey;
- /***/ public String gpgNoCredentialsProvider;
- /***/ public String gpgNoKeyring;
- /***/ public String gpgNoKeyInLegacySecring;
- /***/ public String gpgNoPublicKeyFound;
- /***/ public String gpgNoSecretKeyForPublicKey;
- /***/ public String gpgNotASigningKey;
- /***/ public String gpgKeyInfo;
- /***/ public String gpgSigningCancelled;
/***/ public String headRequiredToStash;
/***/ public String hoursAgo;
/***/ public String httpConfigCannotNormalizeURL;
@@ -384,6 +376,7 @@ public class JGitText extends TranslationBundle {
/***/ public String invalidGitModules;
/***/ public String invalidGitType;
/***/ public String invalidHexString;
+ /***/ public String invalidHomeDirectory;
/***/ public String invalidHooksPath;
/***/ public String invalidId;
/***/ public String invalidId0;
@@ -645,6 +638,7 @@ public class JGitText extends TranslationBundle {
/***/ public String shortReadOfOptionalDIRCExtensionExpectedAnotherBytes;
/***/ public String shortSkipOfBlock;
/***/ public String signingNotSupportedOnTag;
+ /***/ public String signingServiceUnavailable;
/***/ public String similarityScoreMustBeWithinBounds;
/***/ public String skipMustBeNonNegative;
/***/ public String skipNotAccessiblePath;
@@ -655,7 +649,6 @@ public class JGitText extends TranslationBundle {
/***/ public String sourceRefNotSpecifiedForRefspec;
/***/ public String squashCommitNotUpdatingHEAD;
/***/ public String sshCommandFailed;
- /***/ public String sshUserNameError;
/***/ public String sslFailureExceptionMessage;
/***/ public String sslFailureInfo;
/***/ public String sslFailureCause;
@@ -716,7 +709,6 @@ public class JGitText extends TranslationBundle {
/***/ public String transportProtoSSH;
/***/ public String transportProtoTest;
/***/ public String transportProvidedRefWithNoObjectId;
- /***/ public String transportSSHRetryInterrupt;
/***/ public String treeEntryAlreadyExists;
/***/ public String treeFilterMarkerTooManyFilters;
/***/ public String treeWalkMustHaveExactlyTwoTrees;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/AddToBitmapWithCacheFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/AddToBitmapWithCacheFilter.java
new file mode 100644
index 0000000000..d7ccadfbe7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/AddToBitmapWithCacheFilter.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2020, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.revwalk;
+
+import org.eclipse.jgit.lib.BitmapIndex.Bitmap;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.revwalk.filter.RevFilter;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevFlag;
+
+/**
+ * A RevFilter that adds the visited commits to {@code bitmap} as a side effect.
+ * <p>
+ * When the walk hits a commit that is the same as {@code cachedCommit} or is
+ * part of {@code bitmap}'s BitmapIndex, that entire bitmap is ORed into
+ * {@code bitmap} and the commit and its parents are marked as SEEN so that the
+ * walk does not have to visit its ancestors. This ensures the walk is very
+ * short if there is good bitmap coverage.
+ */
+public class AddToBitmapWithCacheFilter extends RevFilter {
+ private final AnyObjectId cachedCommit;
+
+ private final Bitmap cachedBitmap;
+
+ private final BitmapBuilder bitmap;
+
+ /**
+ * Create a filter with a cached BitmapCommit that adds visited commits to
+ * the given bitmap.
+ *
+ * @param cachedCommit
+ * the cached commit
+ * @param cachedBitmap
+ * the bitmap corresponds to {@code cachedCommit}}
+ * @param bitmap
+ * bitmap to write visited commits to
+ */
+ public AddToBitmapWithCacheFilter(AnyObjectId cachedCommit,
+ Bitmap cachedBitmap,
+ BitmapBuilder bitmap) {
+ this.cachedCommit = cachedCommit;
+ this.cachedBitmap = cachedBitmap;
+ this.bitmap = bitmap;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final boolean include(RevWalk rw, RevCommit c) {
+ Bitmap visitedBitmap;
+
+ if (bitmap.contains(c)) {
+ // already included
+ } else if ((visitedBitmap = bitmap.getBitmapIndex()
+ .getBitmap(c)) != null) {
+ bitmap.or(visitedBitmap);
+ } else if (cachedCommit.equals(c)) {
+ bitmap.or(cachedBitmap);
+ } else {
+ bitmap.addObject(c, Constants.OBJ_COMMIT);
+ return true;
+ }
+
+ for (RevCommit p : c.getParents()) {
+ p.add(RevFlag.SEEN);
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final RevFilter clone() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final boolean requiresCommitBody() {
+ return false;
+ }
+}
+
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java
index 6aa1a0e8ea..0d3a2b4f12 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java
@@ -252,6 +252,11 @@ public class BitmapIndexImpl implements BitmapIndex {
return bitmapIndex;
}
+ @Override
+ public EWAHCompressedBitmap retrieveCompressed() {
+ return build().retrieveCompressed();
+ }
+
private EWAHCompressedBitmap ewahBitmap(Bitmap other) {
if (other instanceof CompressedBitmap) {
CompressedBitmap b = (CompressedBitmap) other;
@@ -372,7 +377,8 @@ public class BitmapIndexImpl implements BitmapIndex {
};
}
- EWAHCompressedBitmap getEwahCompressedBitmap() {
+ @Override
+ public EWAHCompressedBitmap retrieveCompressed() {
return bitmap;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index 6a822d570a..265b71dd2a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -19,8 +19,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.util.ArrayList;
@@ -685,47 +685,46 @@ public class ObjectDirectory extends FileObjectDatabase {
FileUtils.delete(tmp, FileUtils.RETRY);
return InsertLooseObjectResult.EXISTS_LOOSE;
}
+
try {
- Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst),
- StandardCopyOption.ATOMIC_MOVE);
- dst.setReadOnly();
- unpackedObjectCache.add(id);
- return InsertLooseObjectResult.INSERTED;
- } catch (AtomicMoveNotSupportedException e) {
- LOG.error(e.getMessage(), e);
+ return tryMove(tmp, dst, id);
+ } catch (NoSuchFileException e) {
+ // It's possible the directory doesn't exist yet as the object
+ // directories are always lazily created. Note that we try the
+ // rename/move first as the directory likely does exist.
+ //
+ // Create the directory.
+ //
+ FileUtils.mkdir(dst.getParentFile(), true);
} catch (IOException e) {
- // ignore
+ // Any other IO error is considered a failure.
+ //
+ LOG.error(e.getMessage(), e);
+ FileUtils.delete(tmp, FileUtils.RETRY);
+ return InsertLooseObjectResult.FAILURE;
}
- // Maybe the directory doesn't exist yet as the object
- // directories are always lazily created. Note that we
- // try the rename first as the directory likely does exist.
- //
- FileUtils.mkdir(dst.getParentFile(), true);
try {
- Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst),
- StandardCopyOption.ATOMIC_MOVE);
- dst.setReadOnly();
- unpackedObjectCache.add(id);
- return InsertLooseObjectResult.INSERTED;
- } catch (AtomicMoveNotSupportedException e) {
- LOG.error(e.getMessage(), e);
+ return tryMove(tmp, dst, id);
} catch (IOException e) {
- LOG.debug(e.getMessage(), e);
- }
-
- if (!createDuplicate && has(id)) {
+ // The object failed to be renamed into its proper location and
+ // it doesn't exist in the repository either. We really don't
+ // know what went wrong, so fail.
+ //
+ LOG.error(e.getMessage(), e);
FileUtils.delete(tmp, FileUtils.RETRY);
- return InsertLooseObjectResult.EXISTS_PACKED;
+ return InsertLooseObjectResult.FAILURE;
}
+ }
- // The object failed to be renamed into its proper
- // location and it doesn't exist in the repository
- // either. We really don't know what went wrong, so
- // fail.
- //
- FileUtils.delete(tmp, FileUtils.RETRY);
- return InsertLooseObjectResult.FAILURE;
+ private InsertLooseObjectResult tryMove(File tmp, File dst,
+ ObjectId id)
+ throws IOException {
+ Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst),
+ StandardCopyOption.ATOMIC_MOVE);
+ dst.setReadOnly();
+ unpackedObjectCache.add(id);
+ return InsertLooseObjectResult.INSERTED;
}
boolean searchPacksAgain(PackList old) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
index c908e6a244..e6b2cc12f4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
@@ -14,7 +14,6 @@ package org.eclipse.jgit.internal.storage.file;
import java.io.EOFException;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
@@ -116,7 +115,7 @@ class ObjectDirectoryInserter extends ObjectInserter {
private ObjectId insertOneObject(
File tmp, ObjectId id, boolean createDuplicate)
- throws IOException, ObjectWritingException {
+ throws IOException {
switch (db.insertUnpackedObject(tmp, id, createDuplicate)) {
case INSERTED:
case EXISTS_PACKED:
@@ -163,18 +162,16 @@ class ObjectDirectoryInserter extends ObjectInserter {
}
}
- @SuppressWarnings("resource" /* java 7 */)
private File toTemp(final SHA1 md, final int type, long len,
- final InputStream is) throws IOException, FileNotFoundException,
- Error {
+ final InputStream is) throws IOException {
boolean delete = true;
File tmp = newTempFile();
- try {
- FileOutputStream fOut = new FileOutputStream(tmp);
+ try (FileOutputStream fOut = new FileOutputStream(tmp)) {
try {
OutputStream out = fOut;
- if (config.getFSyncObjectFiles())
+ if (config.getFSyncObjectFiles()) {
out = Channels.newOutputStream(fOut.getChannel());
+ }
DeflaterOutputStream cOut = compress(out);
SHA1OutputStream dOut = new SHA1OutputStream(cOut, md);
writeHeader(dOut, type, len);
@@ -182,53 +179,54 @@ class ObjectDirectoryInserter extends ObjectInserter {
final byte[] buf = buffer();
while (len > 0) {
int n = is.read(buf, 0, (int) Math.min(len, buf.length));
- if (n <= 0)
+ if (n <= 0) {
throw shortInput(len);
+ }
dOut.write(buf, 0, n);
len -= n;
}
dOut.flush();
cOut.finish();
} finally {
- if (config.getFSyncObjectFiles())
+ if (config.getFSyncObjectFiles()) {
fOut.getChannel().force(true);
- fOut.close();
+ }
}
delete = false;
return tmp;
} finally {
- if (delete)
+ if (delete) {
FileUtils.delete(tmp, FileUtils.RETRY);
+ }
}
}
- @SuppressWarnings("resource" /* java 7 */)
private File toTemp(final int type, final byte[] buf, final int pos,
- final int len) throws IOException, FileNotFoundException {
+ final int len) throws IOException {
boolean delete = true;
File tmp = newTempFile();
- try {
- FileOutputStream fOut = new FileOutputStream(tmp);
+ try (FileOutputStream fOut = new FileOutputStream(tmp)) {
try {
OutputStream out = fOut;
- if (config.getFSyncObjectFiles())
+ if (config.getFSyncObjectFiles()) {
out = Channels.newOutputStream(fOut.getChannel());
+ }
DeflaterOutputStream cOut = compress(out);
writeHeader(cOut, type, len);
cOut.write(buf, pos, len);
cOut.finish();
} finally {
- if (config.getFSyncObjectFiles())
+ if (config.getFSyncObjectFiles()) {
fOut.getChannel().force(true);
- fOut.close();
+ }
}
-
delete = false;
return tmp;
} finally {
- if (delete)
+ if (delete) {
FileUtils.delete(tmp, FileUtils.RETRY);
+ }
}
}
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 9538cc5e0a..5666b57609 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
@@ -11,17 +11,16 @@
package org.eclipse.jgit.internal.storage.file;
import java.text.MessageFormat;
+import java.util.ArrayList;
import java.util.Collections;
-import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
-import java.util.NoSuchElementException;
import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl.CompressedBitmap;
+import org.eclipse.jgit.internal.storage.pack.BitmapCommit;
import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.BitmapIndex.Bitmap;
-import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdOwnerMap;
@@ -41,8 +40,12 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
private final EWAHCompressedBitmap blobs;
private final EWAHCompressedBitmap tags;
private final BlockList<PositionEntry> byOffset;
- final BlockList<StoredBitmap>
- byAddOrder = new BlockList<>();
+
+ private final LinkedList<StoredBitmap>
+ bitmapsToWriteXorBuffer = new LinkedList<>();
+
+ private List<StoredEntry> bitmapsToWrite = new ArrayList<>();
+
final ObjectIdOwnerMap<PositionEntry>
positionEntries = new ObjectIdOwnerMap<>();
@@ -134,16 +137,64 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
* the flags to be stored with the bitmap
*/
public void addBitmap(AnyObjectId objectId, Bitmap bitmap, int flags) {
- if (bitmap instanceof BitmapBuilder)
- bitmap = ((BitmapBuilder) bitmap).build();
+ addBitmap(objectId, bitmap.retrieveCompressed(), flags);
+ }
- EWAHCompressedBitmap compressed;
- if (bitmap instanceof CompressedBitmap)
- compressed = ((CompressedBitmap) bitmap).getEwahCompressedBitmap();
- else
- throw new IllegalArgumentException(bitmap.getClass().toString());
+ /**
+ * Processes a commit and prepares its bitmap to write to the bitmap index
+ * file.
+ *
+ * @param c
+ * the commit corresponds to the bitmap.
+ * @param bitmap
+ * the bitmap to be written.
+ * @param flags
+ * the flags of the commit.
+ */
+ public void processBitmapForWrite(BitmapCommit c, Bitmap bitmap,
+ int flags) {
+ EWAHCompressedBitmap compressed = bitmap.retrieveCompressed();
+ compressed.trim();
+ StoredBitmap newest = new StoredBitmap(c, compressed, null, flags);
+
+ bitmapsToWriteXorBuffer.add(newest);
+ if (bitmapsToWriteXorBuffer.size() > MAX_XOR_OFFSET_SEARCH) {
+ bitmapsToWrite.add(
+ generateStoredEntry(bitmapsToWriteXorBuffer.pollFirst()));
+ }
- addBitmap(objectId, compressed, flags);
+ if (c.isAddToIndex()) {
+ // The Bitmap map in the base class is used to make revwalks
+ // efficient, so only add bitmaps that keep it efficient without
+ // bloating memory.
+ addBitmap(c, bitmap, flags);
+ }
+ }
+
+ private StoredEntry generateStoredEntry(StoredBitmap bitmapToWrite) {
+ int bestXorOffset = 0;
+ EWAHCompressedBitmap bestBitmap = bitmapToWrite.getBitmap();
+
+ int offset = 1;
+ for (StoredBitmap curr : bitmapsToWriteXorBuffer) {
+ EWAHCompressedBitmap bitmap = curr.getBitmap()
+ .xor(bitmapToWrite.getBitmap());
+ if (bitmap.sizeInBytes() < bestBitmap.sizeInBytes()) {
+ bestBitmap = bitmap;
+ bestXorOffset = offset;
+ }
+ offset++;
+ }
+
+ PositionEntry entry = positionEntries.get(bitmapToWrite);
+ if (entry == null) {
+ throw new IllegalStateException();
+ }
+ bestBitmap.trim();
+ StoredEntry result = new StoredEntry(entry.namePosition, bestBitmap,
+ bestXorOffset, bitmapToWrite.getFlags());
+
+ return result;
}
/**
@@ -161,7 +212,6 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
bitmap.trim();
StoredBitmap result = new StoredBitmap(objectId, bitmap, null, flags);
getBitmaps().add(result);
- byAddOrder.add(result);
}
/** {@inheritDoc} */
@@ -247,15 +297,18 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
/** {@inheritDoc} */
@Override
public int getBitmapCount() {
- return getBitmaps().size();
+ return bitmapsToWriteXorBuffer.size() + bitmapsToWrite.size();
}
/**
* Remove all the bitmaps entries added.
+ *
+ * @param size
+ * the expected number of bitmap entries to be written.
*/
- public void clearBitmaps() {
- byAddOrder.clear();
+ public void resetBitmaps(int size) {
getBitmaps().clear();
+ bitmapsToWrite = new ArrayList<>(size);
}
/** {@inheritDoc} */
@@ -265,64 +318,18 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
}
/**
- * Get an iterator over the xor compressed entries.
+ * Get list of xor compressed entries that need to be written.
*
- * @return an iterator over the xor compressed entries.
+ * @return a list of the xor compressed entries.
*/
- public Iterable<StoredEntry> getCompressedBitmaps() {
- // Add order is from oldest to newest. The reverse add order is the
- // output order.
- return () -> new Iterator<StoredEntry>() {
-
- private int index = byAddOrder.size() - 1;
-
- @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;
- }
-
- 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());
- }
+ public List<StoredEntry> getCompressedBitmaps() {
+ while (!bitmapsToWriteXorBuffer.isEmpty()) {
+ bitmapsToWrite.add(
+ generateStoredEntry(bitmapsToWriteXorBuffer.pollFirst()));
+ }
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
+ Collections.reverse(bitmapsToWrite);
+ return bitmapsToWrite;
}
/** Data object for the on disk representation of a bitmap entry. */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
index 273eeef7e5..4b25284517 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
@@ -18,7 +18,6 @@ import org.eclipse.jgit.internal.storage.file.BasePackBitmapIndex.StoredBitmap;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.BitmapIndex;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdOwnerMap;
import com.googlecode.javaewah.EWAHCompressedBitmap;
import com.googlecode.javaewah.IntIterator;
@@ -34,7 +33,6 @@ public class PackBitmapIndexRemapper extends PackBitmapIndex
private final BasePackBitmapIndex oldPackIndex;
final PackBitmapIndex newPackIndex;
- private final ObjectIdOwnerMap<StoredBitmap> convertedBitmaps;
private final BitSet inflated;
private final int[] prevToNewMapping;
@@ -65,7 +63,6 @@ public class PackBitmapIndexRemapper extends PackBitmapIndex
private PackBitmapIndexRemapper(PackBitmapIndex newPackIndex) {
this.oldPackIndex = null;
this.newPackIndex = newPackIndex;
- this.convertedBitmaps = null;
this.inflated = null;
this.prevToNewMapping = null;
}
@@ -74,7 +71,6 @@ public class PackBitmapIndexRemapper extends PackBitmapIndex
BasePackBitmapIndex oldPackIndex, PackBitmapIndex newPackIndex) {
this.oldPackIndex = oldPackIndex;
this.newPackIndex = newPackIndex;
- convertedBitmaps = new ObjectIdOwnerMap<>();
inflated = new BitSet(newPackIndex.getObjectCount());
prevToNewMapping = new int[oldPackIndex.getObjectCount()];
@@ -152,10 +148,6 @@ public class PackBitmapIndexRemapper extends PackBitmapIndex
if (bitmap != null || oldPackIndex == null)
return bitmap;
- StoredBitmap stored = convertedBitmaps.get(objectId);
- if (stored != null)
- return stored.getBitmap();
-
StoredBitmap oldBitmap = oldPackIndex.getBitmaps().get(objectId);
if (oldBitmap == null)
return null;
@@ -168,8 +160,6 @@ public class PackBitmapIndexRemapper extends PackBitmapIndex
inflated.set(prevToNewMapping[i.next()]);
bitmap = inflated.toEWAHCompressedBitmap();
bitmap.trim();
- convertedBitmaps.add(
- new StoredBitmap(objectId, bitmap, null, oldBitmap.getFlags()));
return bitmap;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
index 852302f00c..80c8e10dec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
@@ -470,7 +470,9 @@ public class WindowCache {
mbean = new StatsRecorderImpl();
statsRecorder = mbean;
- Monitoring.registerMBean(mbean, "block_cache"); //$NON-NLS-1$
+ if (cfg.getExposeStatsViaJmx()) {
+ Monitoring.registerMBean(mbean, "block_cache"); //$NON-NLS-1$
+ }
if (maxFiles < 1)
throw new IllegalArgumentException(JGitText.get().openFilesMustBeAtLeast1);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BitmapCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BitmapCommit.java
new file mode 100644
index 0000000000..33c478e4ab
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BitmapCommit.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2020, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.pack;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * A commit object for which a bitmap index should be built.
+ */
+public final class BitmapCommit extends ObjectId {
+
+ private final boolean reuseWalker;
+
+ private final int flags;
+
+ private final boolean addToIndex;
+
+ BitmapCommit(AnyObjectId objectId, boolean reuseWalker, int flags) {
+ super(objectId);
+ this.reuseWalker = reuseWalker;
+ this.flags = flags;
+ this.addToIndex = false;
+ }
+
+ BitmapCommit(AnyObjectId objectId, boolean reuseWalker, int flags,
+ boolean addToIndex) {
+ super(objectId);
+ this.reuseWalker = reuseWalker;
+ this.flags = flags;
+ this.addToIndex = addToIndex;
+ }
+
+ boolean isReuseWalker() {
+ return reuseWalker;
+ }
+
+ int getFlags() {
+ return flags;
+ }
+
+ /**
+ * Whether corresponding bitmap should be added to PackBitmapIndexBuilder.
+ *
+ * @return true if the corresponding bitmap should be added to
+ * PackBitmapIndexBuilder.
+ */
+ public boolean isAddToIndex() {
+ return addToIndex;
+ }
+
+ /**
+ * Get a builder of BitmapCommit whose object id is {@code objId}.
+ *
+ * @param objId
+ * the object id of the BitmapCommit
+ * @return a BitmapCommit builder with object id set.
+ */
+ public static Builder newBuilder(AnyObjectId objId) {
+ return new Builder().setId(objId);
+ }
+
+ /**
+ * Get a builder of BitmapCommit whose fields are copied from
+ * {@code commit}.
+ *
+ * @param commit
+ * the bitmap commit the builder is copying from
+ * @return a BitmapCommit build with fields copied from an existing bitmap
+ * commit.
+ */
+ public static Builder copyFrom(BitmapCommit commit) {
+ return new Builder().setId(commit)
+ .setReuseWalker(commit.isReuseWalker())
+ .setFlags(commit.getFlags())
+ .setAddToIndex(commit.isAddToIndex());
+ }
+
+ /**
+ * Builder of BitmapCommit.
+ */
+ public static class Builder {
+ private AnyObjectId objectId;
+
+ private boolean reuseWalker;
+
+ private int flags;
+
+ private boolean addToIndex;
+
+ // Prevent default constructor.
+ private Builder() {
+ }
+
+ /**
+ * Set objectId of the builder.
+ *
+ * @param objectId
+ * the object id of the BitmapCommit
+ * @return the builder itself
+ */
+ public Builder setId(AnyObjectId objectId) {
+ this.objectId = objectId;
+ return this;
+ }
+
+ /**
+ * Set reuseWalker of the builder.
+ *
+ * @param reuseWalker
+ * whether the BitmapCommit should reuse bitmap walker when
+ * walking objects
+ * @return the builder itself
+ */
+ public Builder setReuseWalker(boolean reuseWalker) {
+ this.reuseWalker = reuseWalker;
+ return this;
+ }
+
+ /**
+ * Set flags of the builder.
+ *
+ * @param flags
+ * the flags of the BitmapCommit
+ * @return the builder itself
+ */
+ public Builder setFlags(int flags) {
+ this.flags = flags;
+ return this;
+ }
+
+ /**
+ * Set whether whether the bitmap of the BitmapCommit should be added to
+ * PackBitmapIndexBuilder when building bitmap index file.
+ *
+ * @param addToIndex
+ * whether the bitmap of the BitmapCommit should be added to
+ * PackBitmapIndexBuilder when building bitmap index file
+ * @return the builder itself
+ */
+ public Builder setAddToIndex(boolean addToIndex) {
+ this.addToIndex = addToIndex;
+ return this;
+ }
+
+ /**
+ * Builds BitmapCommit from the builder.
+ *
+ * @return the new BitmapCommit.
+ */
+ public BitmapCommit build() {
+ return new BitmapCommit(objectId, reuseWalker, flags,
+ addToIndex);
+ }
+ }
+} \ No newline at end of file
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 75dd345f46..824c62ad9a 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
@@ -2313,14 +2313,14 @@ public class PackWriter implements AutoCloseable {
PackWriterBitmapPreparer bitmapPreparer = new PackWriterBitmapPreparer(
reader, writeBitmaps, pm, stats.interestingObjects, config);
- Collection<PackWriterBitmapPreparer.BitmapCommit> selectedCommits = bitmapPreparer
+ Collection<BitmapCommit> selectedCommits = bitmapPreparer
.selectCommits(numCommits, excludeFromBitmapSelection);
beginPhase(PackingPhase.BUILDING_BITMAPS, pm, selectedCommits.size());
BitmapWalker walker = bitmapPreparer.newBitmapWalker();
AnyObjectId last = null;
- for (PackWriterBitmapPreparer.BitmapCommit cmit : selectedCommits) {
+ for (BitmapCommit cmit : selectedCommits) {
if (!cmit.isReuseWalker()) {
walker = bitmapPreparer.newBitmapWalker();
}
@@ -2331,8 +2331,14 @@ public class PackWriter implements AutoCloseable {
throw new IllegalStateException(MessageFormat.format(
JGitText.get().bitmapMissingObject, cmit.name(),
last.name()));
- last = cmit;
- writeBitmaps.addBitmap(cmit, bitmap.build(), cmit.getFlags());
+ last = BitmapCommit.copyFrom(cmit).build();
+ writeBitmaps.processBitmapForWrite(cmit, bitmap.build(),
+ cmit.getFlags());
+
+ // The bitmap walker should stop when the walk hits the previous
+ // commit, which saves time.
+ walker.setPrevCommit(last);
+ walker.setPrevBitmap(bitmap);
pm.update(1);
}
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 51b4993e26..f1ede2acff 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
@@ -58,6 +58,8 @@ class PackWriterBitmapPreparer {
private static final int DAY_IN_SECONDS = 24 * 60 * 60;
+ private static final int DISTANCE_THRESHOLD = 2000;
+
private static final Comparator<RevCommit> ORDER_BY_REVERSE_TIMESTAMP = (
RevCommit a, RevCommit b) -> Integer
.signum(b.getCommitTime() - a.getCommitTime());
@@ -244,6 +246,7 @@ class PackWriterBitmapPreparer {
// This commit is selected.
// Calculate where to look for the next one.
int flags = nextFlg;
+ int currDist = distanceFromTip;
nextIn = nextSpan(distanceFromTip);
nextFlg = nextIn == distantCommitSpan
? PackBitmapIndex.FLAG_REUSE
@@ -279,8 +282,17 @@ class PackWriterBitmapPreparer {
longestAncestorChain = new ArrayList<>();
chains.add(longestAncestorChain);
}
- longestAncestorChain.add(new BitmapCommit(c,
- !longestAncestorChain.isEmpty(), flags));
+
+ // The commit bc should reuse bitmap walker from its close
+ // ancestor. And the bitmap of bc should only be added to
+ // PackBitmapIndexBuilder when it's an old enough
+ // commit,i.e. distance from tip should be greater than
+ // DISTANCE_THRESHOLD, to save memory.
+ BitmapCommit bc = BitmapCommit.newBuilder(c).setFlags(flags)
+ .setAddToIndex(currDist >= DISTANCE_THRESHOLD)
+ .setReuseWalker(!longestAncestorChain.isEmpty())
+ .build();
+ longestAncestorChain.add(bc);
writeBitmaps.addBitmap(c, bitmap, 0);
}
@@ -288,12 +300,12 @@ class PackWriterBitmapPreparer {
selections.addAll(chain);
}
}
- writeBitmaps.clearBitmaps(); // Remove the temporary commit bitmaps.
// Add the remaining peeledWant
for (AnyObjectId remainingWant : selectionHelper.newWants) {
selections.add(new BitmapCommit(remainingWant, false, 0));
}
+ writeBitmaps.resetBitmaps(selections.size()); // Remove the temporary commit bitmaps.
pm.endTask();
return selections;
@@ -468,28 +480,6 @@ class PackWriterBitmapPreparer {
}
/**
- * A commit object for which a bitmap index should be built.
- */
- static final class BitmapCommit extends ObjectId {
- private final boolean reuseWalker;
- private final int flags;
-
- BitmapCommit(AnyObjectId objectId, boolean reuseWalker, int flags) {
- super(objectId);
- this.reuseWalker = reuseWalker;
- this.flags = flags;
- }
-
- boolean isReuseWalker() {
- return reuseWalker;
- }
-
- int getFlags() {
- return flags;
- }
- }
-
- /**
* Container for state used in the first phase of selecting commits, which
* walks all of the reachable commits via the branch tips that are not
* covered by a previous pack's bitmaps ({@code newWants}) and stores them
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java
index 4de6c28709..4747be3544 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java
@@ -280,7 +280,7 @@ public abstract class ReftableDatabase {
/**
* Returns all refs that resolve directly to the given {@link ObjectId}.
- * Includes peeled {@linkObjectId}s.
+ * Includes peeled {@link ObjectId}s.
*
* @param id
* {@link ObjectId} to resolve
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/internal/FullConnectivityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/connectivity/FullConnectivityChecker.java
index 60d8f452ba..b76e3a3caa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/internal/FullConnectivityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/connectivity/FullConnectivityChecker.java
@@ -8,7 +8,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
-package org.eclipse.jgit.transport.internal;
+package org.eclipse.jgit.internal.transport.connectivity;
import java.io.IOException;
import java.util.Set;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/connectivity/IterativeConnectivityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/connectivity/IterativeConnectivityChecker.java
new file mode 100644
index 0000000000..b44c4ae5cb
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/connectivity/IterativeConnectivityChecker.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2019, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.transport.connectivity;
+
+import static java.util.stream.Collectors.toList;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ConnectivityChecker;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/**
+ * Implementation of connectivity checker which tries to do check with smaller
+ * set of references first and if it fails will fall back to check against all
+ * advertised references.
+ *
+ * This is useful for big repos with enormous number of references.
+ */
+public class IterativeConnectivityChecker implements ConnectivityChecker {
+ private static final int MAXIMUM_PARENTS_TO_CHECK = 128;
+
+ private final ConnectivityChecker delegate;
+
+ private Set<ObjectId> forcedHaves = Collections.emptySet();
+
+ /**
+ * @param delegate
+ * Delegate checker which will be called for actual checks.
+ */
+ public IterativeConnectivityChecker(ConnectivityChecker delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void checkConnectivity(ConnectivityCheckInfo connectivityCheckInfo,
+ Set<ObjectId> advertisedHaves, ProgressMonitor pm)
+ throws MissingObjectException, IOException {
+ try {
+ Set<ObjectId> newRefs = new HashSet<>();
+ Set<ObjectId> expectedParents = new HashSet<>();
+
+ getAllObjectIds(connectivityCheckInfo.getCommands())
+ .forEach(oid -> {
+ if (advertisedHaves.contains(oid)) {
+ expectedParents.add(oid);
+ } else {
+ newRefs.add(oid);
+ }
+ });
+ if (!newRefs.isEmpty()) {
+ expectedParents.addAll(extractAdvertisedParentCommits(newRefs,
+ advertisedHaves, connectivityCheckInfo.getWalk()));
+ }
+
+ expectedParents.addAll(forcedHaves);
+
+ if (!expectedParents.isEmpty()) {
+ delegate.checkConnectivity(connectivityCheckInfo,
+ expectedParents, pm);
+ return;
+ }
+ } catch (MissingObjectException e) {
+ // This is fine, retry with all haves.
+ }
+ delegate.checkConnectivity(connectivityCheckInfo, advertisedHaves, pm);
+ }
+
+ private static Stream<ObjectId> getAllObjectIds(
+ List<ReceiveCommand> commands) {
+ return commands.stream().flatMap(cmd -> {
+ if (cmd.getType() == ReceiveCommand.Type.UPDATE || cmd
+ .getType() == ReceiveCommand.Type.UPDATE_NONFASTFORWARD) {
+ return Stream.of(cmd.getOldId(), cmd.getNewId());
+ } else if (cmd.getType() == ReceiveCommand.Type.CREATE) {
+ return Stream.of(cmd.getNewId());
+ }
+ return Stream.of();
+ });
+ }
+
+ /**
+ * Sets additional haves that client can depend on (e.g. gerrit changes).
+ *
+ * @param forcedHaves
+ * Haves server expects client to depend on.
+ */
+ public void setForcedHaves(Set<ObjectId> forcedHaves) {
+ this.forcedHaves = Collections.unmodifiableSet(forcedHaves);
+ }
+
+ private static Set<ObjectId> extractAdvertisedParentCommits(
+ Set<ObjectId> newRefs, Set<ObjectId> advertisedHaves, RevWalk rw)
+ throws MissingObjectException, IOException {
+ Set<ObjectId> advertisedParents = new HashSet<>();
+ for (ObjectId newRef : newRefs) {
+ RevObject object = rw.parseAny(newRef);
+ if (object instanceof RevCommit) {
+ int numberOfParentsToCheck = 0;
+ Queue<RevCommit> parents = new ArrayDeque<>(
+ MAXIMUM_PARENTS_TO_CHECK);
+ parents.addAll(
+ parseParents(((RevCommit) object).getParents(), rw));
+ // Looking through a chain of ancestors handles the case where a
+ // series of commits is sent in a single push for a new branch.
+ while (!parents.isEmpty()) {
+ RevCommit parentCommit = parents.poll();
+ if (advertisedHaves.contains(parentCommit.getId())) {
+ advertisedParents.add(parentCommit.getId());
+ } else if (numberOfParentsToCheck < MAXIMUM_PARENTS_TO_CHECK) {
+ RevCommit[] grandParents = parentCommit.getParents();
+ numberOfParentsToCheck += grandParents.length;
+ parents.addAll(parseParents(grandParents, rw));
+ }
+ }
+ }
+ }
+ return advertisedParents;
+ }
+
+ private static List<RevCommit> parseParents(RevCommit[] parents,
+ RevWalk rw) {
+ return Arrays.stream(parents).map((commit) -> {
+ try {
+ return rw.parseCommit(commit);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }).collect(toList());
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/internal/DelegatingSSLSocketFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/DelegatingSSLSocketFactory.java
index d25ecd459d..5aab61ad05 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/internal/DelegatingSSLSocketFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/DelegatingSSLSocketFactory.java
@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-package org.eclipse.jgit.transport.internal;
+package org.eclipse.jgit.internal.transport.http;
import java.io.IOException;
import java.net.InetAddress;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
index 2fbc9122f1..98c63cdcdd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
@@ -32,6 +32,7 @@ import java.util.TreeSet;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.errors.InvalidPatternException;
import org.eclipse.jgit.fnmatch.FileNameMatcher;
+import org.eclipse.jgit.transport.SshConfigStore;
import org.eclipse.jgit.transport.SshConstants;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.StringUtils;
@@ -80,7 +81,7 @@ import org.eclipse.jgit.util.SystemReader;
* @see <a href="http://man.openbsd.org/OpenBSD-current/man5/ssh_config.5">man
* ssh-config</a>
*/
-public class OpenSshConfigFile {
+public class OpenSshConfigFile implements SshConfigStore {
/**
* "Host" name of the HostEntry for the default options before the first
@@ -152,8 +153,9 @@ public class OpenSshConfigFile {
* the user supplied; <= 0 if none
* @param userName
* the user supplied, may be {@code null} or empty if none given
- * @return r configuration for the requested name.
+ * @return the configuration for the requested name.
*/
+ @Override
@NonNull
public HostEntry lookup(@NonNull String hostName, int port,
String userName) {
@@ -446,7 +448,7 @@ public class OpenSshConfigFile {
* of several matching host entries, %-substitutions, and ~ replacement have
* all been done.
*/
- public static class HostEntry {
+ public static class HostEntry implements SshConfigStore.HostConfig {
/**
* Keys that can be specified multiple times, building up a list. (I.e.,
@@ -489,7 +491,7 @@ public class OpenSshConfigFile {
private Map<String, List<String>> listOptions;
/**
- * Retrieves the value of a single-valued key, or the first is the key
+ * Retrieves the value of a single-valued key, or the first if the key
* has multiple values. Keys are case-insensitive, so
* {@code getValue("HostName") == getValue("HOSTNAME")}.
*
@@ -497,6 +499,7 @@ public class OpenSshConfigFile {
* to get the value of
* @return the value, or {@code null} if none
*/
+ @Override
public String getValue(String key) {
String result = options != null ? options.get(key) : null;
if (result == null) {
@@ -524,6 +527,7 @@ public class OpenSshConfigFile {
* to get the values of
* @return a possibly empty list of values
*/
+ @Override
public List<String> getValues(String key) {
List<String> values = listOptions != null ? listOptions.get(key)
: null;
@@ -778,6 +782,7 @@ public class OpenSshConfigFile {
*
* @return all single-valued options
*/
+ @Override
@NonNull
public Map<String, String> getOptions() {
if (options == null) {
@@ -792,6 +797,7 @@ public class OpenSshConfigFile {
*
* @return all multi-valued options
*/
+ @Override
@NonNull
public Map<String, List<String>> getMultiValuedOptions() {
if (listOptions == null && multiOptions == null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
index f61286d6da..f6695bdf7d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
@@ -14,6 +14,8 @@ import java.util.Iterator;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
+import com.googlecode.javaewah.EWAHCompressedBitmap;
+
/**
* A compressed bitmap representation of the entire object graph.
*
@@ -81,6 +83,14 @@ public interface BitmapIndex {
*/
@Override
Iterator<BitmapObject> iterator();
+
+ /**
+ * Returns the corresponding raw compressed EWAH bitmap of the bitmap.
+ *
+ * @return the corresponding {@code EWAHCompressedBitmap}
+ * @since 5.8
+ */
+ EWAHCompressedBitmap retrieveCompressed();
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java
index d953a01945..5b32cf0b5f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java
@@ -9,11 +9,16 @@
*/
package org.eclipse.jgit.lib;
+import java.util.Iterator;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.CanceledException;
-import org.eclipse.jgit.lib.internal.BouncyCastleGpgSigner;
import org.eclipse.jgit.transport.CredentialsProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Creates GPG signatures for Git objects.
@@ -21,8 +26,23 @@ import org.eclipse.jgit.transport.CredentialsProvider;
* @since 5.3
*/
public abstract class GpgSigner {
+ private static final Logger LOG = LoggerFactory.getLogger(GpgSigner.class);
+
+ private static GpgSigner defaultSigner = loadGpgSigner();
- private static GpgSigner defaultSigner = new BouncyCastleGpgSigner();
+ private static GpgSigner loadGpgSigner() {
+ try {
+ ServiceLoader<GpgSigner> loader = ServiceLoader
+ .load(GpgSigner.class);
+ Iterator<GpgSigner> iter = loader.iterator();
+ if (iter.hasNext()) {
+ return iter.next();
+ }
+ } catch (ServiceConfigurationError e) {
+ LOG.error(e.getMessage(), e);
+ }
+ return null;
+ }
/**
* Get the default signer, or <code>null</code>.
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 ff5a84ca6e..6832c9cd80 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -441,7 +441,7 @@ public abstract class RefDatabase {
/**
* Returns all refs that resolve directly to the given {@link ObjectId}.
- * Includes peeled {@linkObjectId}s. This is the inverse lookup of
+ * Includes peeled {@link ObjectId}s. This is the inverse lookup of
* {@link #exactRef(String...)}.
*
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKey.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKey.java
deleted file mode 100644
index 8601d7c94f..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKey.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2018, Salesforce. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.lib.internal;
-
-import java.nio.file.Path;
-
-import org.bouncycastle.openpgp.PGPSecretKey;
-
-/**
- * Container which holds a {@link #getSecretKey()} together with the
- * {@link #getOrigin() path it was loaded from}.
- */
-class BouncyCastleGpgKey {
-
- private PGPSecretKey secretKey;
-
- private Path origin;
-
- public BouncyCastleGpgKey(PGPSecretKey secretKey, Path origin) {
- this.secretKey = secretKey;
- this.origin = origin;
- }
-
- public PGPSecretKey getSecretKey() {
- return secretKey;
- }
-
- public Path getOrigin() {
- return origin;
- }
-} \ No newline at end of file
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
deleted file mode 100644
index 8a32299dd3..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java
+++ /dev/null
@@ -1,614 +0,0 @@
-/*
- * Copyright (C) 2018, Salesforce. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.lib.internal;
-
-import static java.nio.file.Files.exists;
-import static java.nio.file.Files.newInputStream;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URISyntaxException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.InvalidPathException;
-import java.nio.file.Path;
-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;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.bouncycastle.gpg.SExprParser;
-import org.bouncycastle.gpg.keybox.BlobType;
-import org.bouncycastle.gpg.keybox.KeyBlob;
-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.PGPKeyFlags;
-import org.bouncycastle.openpgp.PGPPublicKey;
-import org.bouncycastle.openpgp.PGPPublicKeyRing;
-import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
-import org.bouncycastle.openpgp.PGPSecretKey;
-import org.bouncycastle.openpgp.PGPSecretKeyRing;
-import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
-import org.bouncycastle.openpgp.PGPSignature;
-import org.bouncycastle.openpgp.PGPUtil;
-import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
-import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
-import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
-import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
-import org.bouncycastle.openpgp.operator.jcajce.JcePBEProtectionRemoverFactory;
-import org.bouncycastle.util.encoders.Hex;
-import org.eclipse.jgit.annotations.NonNull;
-import org.eclipse.jgit.api.errors.CanceledException;
-import org.eclipse.jgit.errors.UnsupportedCredentialItem;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.util.FS;
-import org.eclipse.jgit.util.StringUtils;
-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
- * <code>~/.gnupg/secring.gpg</code>
- */
-class BouncyCastleGpgKeyLocator {
-
- /** Thrown if a keybox file exists but doesn't contain an OpenPGP key. */
- private static class NoOpenPgpKeyException extends Exception {
-
- private static final long serialVersionUID = 1L;
-
- }
-
- private static final Logger log = LoggerFactory
- .getLogger(BouncyCastleGpgKeyLocator.class);
-
- private static final Path GPG_DIRECTORY = findGpgDirectory();
-
- private static final Path USER_KEYBOX_PATH = GPG_DIRECTORY
- .resolve("pubring.kbx"); //$NON-NLS-1$
-
- private static final Path USER_SECRET_KEY_DIR = GPG_DIRECTORY
- .resolve("private-keys-v1.d"); //$NON-NLS-1$
-
- private static final Path USER_PGP_PUBRING_FILE = GPG_DIRECTORY
- .resolve("pubring.gpg"); //$NON-NLS-1$
-
- private static final Path USER_PGP_LEGACY_SECRING_FILE = GPG_DIRECTORY
- .resolve("secring.gpg"); //$NON-NLS-1$
-
- private final String signingKey;
-
- private BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt;
-
- private static Path findGpgDirectory() {
- SystemReader system = SystemReader.getInstance();
- if (system.isWindows()) {
- // On Windows prefer %APPDATA%\gnupg if it exists, even if Cygwin is
- // used.
- String appData = system.getenv("APPDATA"); //$NON-NLS-1$
- if (appData != null && !appData.isEmpty()) {
- try {
- Path directory = Paths.get(appData).resolve("gnupg"); //$NON-NLS-1$
- if (Files.isDirectory(directory)) {
- return directory;
- }
- } catch (SecurityException | InvalidPathException e) {
- // Ignore and return the default location below.
- }
- }
- }
- // All systems, including Cygwin and even Windows if
- // %APPDATA%\gnupg doesn't exist: ~/.gnupg
- File home = FS.DETECTED.userHome();
- if (home == null) {
- // Oops. What now?
- home = new File(".").getAbsoluteFile(); //$NON-NLS-1$
- }
- return home.toPath().resolve(".gnupg"); //$NON-NLS-1$
- }
-
- /**
- * Create a new key locator for the specified signing key.
- * <p>
- * The signing key must either be a hex representation of a specific key or
- * a user identity substring (eg., email address). All keys in the KeyBox
- * will be looked up in the order as returned by the KeyBox. A key id will
- * be searched before attempting to find a key by user id.
- * </p>
- *
- * @param signingKey
- * the signing key to search for
- * @param passphrasePrompt
- * the provider to use when asking for key passphrase
- */
- public BouncyCastleGpgKeyLocator(String signingKey,
- @NonNull BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt) {
- this.signingKey = signingKey;
- this.passphrasePrompt = passphrasePrompt;
- }
-
- private PGPSecretKey attemptParseSecretKey(Path keyFile,
- PGPDigestCalculatorProvider calculatorProvider,
- PBEProtectionRemoverFactory passphraseProvider,
- PGPPublicKey publicKey) {
- try (InputStream in = newInputStream(keyFile)) {
- return new SExprParser(calculatorProvider).parseSecretKey(
- new BufferedInputStream(in), passphraseProvider, publicKey);
- } catch (IOException | PGPException | ClassCastException e) {
- if (log.isDebugEnabled())
- log.debug("Ignoring unreadable file '{}': {}", keyFile, //$NON-NLS-1$
- e.getMessage(), e);
- return null;
- }
- }
-
- /**
- * Checks whether a given OpenPGP {@code userId} matches a given
- * {@code signingKeySpec}, which is supposed to have one of the formats
- * defined by GPG.
- * <p>
- * Not all formats are supported; only formats starting with '=', '&lt;',
- * '@', and '*' are handled. Any other format results in a case-insensitive
- * substring match.
- * </p>
- *
- * @param userId
- * of a key
- * @param signingKeySpec
- * GPG key identification
- * @return whether the {@code userId} matches
- * @see <a href=
- * "https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html">GPG
- * Documentation: How to Specify a User ID</a>
- */
- static boolean containsSigningKey(String userId, String signingKeySpec) {
- if (StringUtils.isEmptyOrNull(userId)
- || StringUtils.isEmptyOrNull(signingKeySpec)) {
- return false;
- }
- String toMatch = signingKeySpec;
- if (toMatch.startsWith("0x") && toMatch.trim().length() > 2) { //$NON-NLS-1$
- return false; // Explicit fingerprint
- }
- int command = toMatch.charAt(0);
- switch (command) {
- case '=':
- case '<':
- case '@':
- case '*':
- toMatch = toMatch.substring(1);
- if (toMatch.isEmpty()) {
- return false;
- }
- break;
- default:
- break;
- }
- switch (command) {
- case '=':
- return userId.equals(toMatch);
- case '<': {
- int begin = userId.indexOf('<');
- int end = userId.indexOf('>', begin + 1);
- int stop = toMatch.indexOf('>');
- return begin >= 0 && end > begin + 1 && stop > 0
- && userId.substring(begin + 1, end)
- .equals(toMatch.substring(0, stop));
- }
- case '@': {
- int begin = userId.indexOf('<');
- int end = userId.indexOf('>', begin + 1);
- return begin >= 0 && end > begin + 1
- && userId.substring(begin + 1, end).contains(toMatch);
- }
- default:
- if (toMatch.trim().isEmpty()) {
- return false;
- }
- return userId.toLowerCase(Locale.ROOT)
- .contains(toMatch.toLowerCase(Locale.ROOT));
- }
- }
-
- private String toFingerprint(String keyId) {
- if (keyId.startsWith("0x")) { //$NON-NLS-1$
- return keyId.substring(2);
- }
- return keyId;
- }
-
- private PGPPublicKey findPublicKeyByKeyId(KeyBlob keyBlob)
- throws IOException {
- String keyId = toFingerprint(signingKey).toLowerCase(Locale.ROOT);
- if (keyId.isEmpty()) {
- return null;
- }
- for (KeyInformation keyInfo : keyBlob.getKeyInformation()) {
- String fingerprint = Hex.toHexString(keyInfo.getFingerprint())
- .toLowerCase(Locale.ROOT);
- if (fingerprint.endsWith(keyId)) {
- return getPublicKey(keyBlob, keyInfo.getFingerprint());
- }
- }
- return null;
- }
-
- private PGPPublicKey findPublicKeyByUserId(KeyBlob keyBlob)
- throws IOException {
- for (UserID userID : keyBlob.getUserIds()) {
- if (containsSigningKey(userID.getUserIDAsString(), signingKey)) {
- return getSigningPublicKey(keyBlob);
- }
- }
- return null;
- }
-
- /**
- * Finds a public key associated with the signing key.
- *
- * @param keyboxFile
- * the KeyBox file
- * @return publicKey the public key (maybe <code>null</code>)
- * @throws IOException
- * in case of problems reading the file
- * @throws NoSuchAlgorithmException
- * @throws NoSuchProviderException
- * @throws NoOpenPgpKeyException
- * if the file does not contain any OpenPGP key
- */
- private PGPPublicKey findPublicKeyInKeyBox(Path keyboxFile)
- throws IOException, NoSuchAlgorithmException,
- NoSuchProviderException, NoOpenPgpKeyException {
- KeyBox keyBox = readKeyBoxFile(keyboxFile);
- boolean hasOpenPgpKey = false;
- for (KeyBlob keyBlob : keyBox.getKeyBlobs()) {
- if (keyBlob.getType() == BlobType.OPEN_PGP_BLOB) {
- hasOpenPgpKey = true;
- PGPPublicKey key = findPublicKeyByKeyId(keyBlob);
- if (key != null) {
- return key;
- }
- key = findPublicKeyByUserId(keyBlob);
- if (key != null) {
- return key;
- }
- }
- }
- if (!hasOpenPgpKey) {
- throw new NoOpenPgpKeyException();
- }
- return null;
- }
-
- /**
- * If there is a private key directory containing keys, use pubring.kbx or
- * pubring.gpg to find the public key; then try to find the secret key in
- * the directory.
- * <p>
- * If there is no private key directory (or it doesn't contain any keys),
- * try to find the key in secring.gpg directly.
- * </p>
- *
- * @return the secret key
- * @throws IOException
- * in case of issues reading key files
- * @throws NoSuchAlgorithmException
- * @throws NoSuchProviderException
- * @throws PGPException
- * in case of issues finding a key, including no key found
- * @throws CanceledException
- * @throws URISyntaxException
- * @throws UnsupportedCredentialItem
- */
- @NonNull
- public BouncyCastleGpgKey findSecretKey() throws IOException,
- NoSuchAlgorithmException, NoSuchProviderException, PGPException,
- CanceledException, UnsupportedCredentialItem, URISyntaxException {
- BouncyCastleGpgKey key;
- PGPPublicKey publicKey = null;
- if (hasKeyFiles(USER_SECRET_KEY_DIR)) {
- // Use pubring.kbx or pubring.gpg to find the public key, then try
- // the key files in the directory. If the public key was found in
- // pubring.gpg also try secring.gpg to find the secret key.
- if (exists(USER_KEYBOX_PATH)) {
- try {
- publicKey = findPublicKeyInKeyBox(USER_KEYBOX_PATH);
- if (publicKey != null) {
- key = findSecretKeyForKeyBoxPublicKey(publicKey,
- USER_KEYBOX_PATH);
- if (key != null) {
- return key;
- }
- throw new PGPException(MessageFormat.format(
- JGitText.get().gpgNoSecretKeyForPublicKey,
- Long.toHexString(publicKey.getKeyID())));
- }
- throw new PGPException(MessageFormat.format(
- JGitText.get().gpgNoPublicKeyFound, signingKey));
- } catch (NoOpenPgpKeyException e) {
- // There are no OpenPGP keys in the keybox at all: try the
- // pubring.gpg, if it exists.
- if (log.isDebugEnabled()) {
- log.debug("{} does not contain any OpenPGP keys", //$NON-NLS-1$
- USER_KEYBOX_PATH);
- }
- }
- }
- if (exists(USER_PGP_PUBRING_FILE)) {
- publicKey = findPublicKeyInPubring(USER_PGP_PUBRING_FILE);
- if (publicKey != null) {
- // GPG < 2.1 may have both; the agent using the directory
- // and gpg using secring.gpg. GPG >= 2.1 delegates all
- // secret key handling to the agent and doesn't use
- // secring.gpg at all, even if it exists. Which means for us
- // we have to try both since we don't know which GPG version
- // the user has.
- key = findSecretKeyForKeyBoxPublicKey(publicKey,
- USER_PGP_PUBRING_FILE);
- if (key != null) {
- return key;
- }
- }
- }
- if (publicKey == null) {
- throw new PGPException(MessageFormat.format(
- JGitText.get().gpgNoPublicKeyFound, signingKey));
- }
- // We found a public key, but didn't find the secret key in the
- // private key directory. Go try the secring.gpg.
- }
- boolean hasSecring = false;
- if (exists(USER_PGP_LEGACY_SECRING_FILE)) {
- hasSecring = true;
- key = loadKeyFromSecring(USER_PGP_LEGACY_SECRING_FILE);
- if (key != null) {
- return key;
- }
- }
- if (publicKey != null) {
- throw new PGPException(MessageFormat.format(
- JGitText.get().gpgNoSecretKeyForPublicKey,
- Long.toHexString(publicKey.getKeyID())));
- } else if (hasSecring) {
- // publicKey == null: user has _only_ pubring.gpg/secring.gpg.
- throw new PGPException(MessageFormat.format(
- JGitText.get().gpgNoKeyInLegacySecring, signingKey));
- } else {
- throw new PGPException(JGitText.get().gpgNoKeyring);
- }
- }
-
- private boolean hasKeyFiles(Path dir) {
- try (DirectoryStream<Path> contents = Files.newDirectoryStream(dir,
- "*.key")) { //$NON-NLS-1$
- return contents.iterator().hasNext();
- } catch (IOException e) {
- // Not a directory, or something else
- return false;
- }
- }
-
- private BouncyCastleGpgKey loadKeyFromSecring(Path secring)
- throws IOException, PGPException {
- PGPSecretKey secretKey = findSecretKeyInLegacySecring(signingKey,
- secring);
-
- if (secretKey != null) {
- if (!secretKey.isSigningKey()) {
- throw new PGPException(MessageFormat
- .format(JGitText.get().gpgNotASigningKey, signingKey));
- }
- return new BouncyCastleGpgKey(secretKey, secring);
- }
- return null;
- }
-
- private BouncyCastleGpgKey findSecretKeyForKeyBoxPublicKey(
- PGPPublicKey publicKey, Path userKeyboxPath)
- throws PGPException, CanceledException, UnsupportedCredentialItem,
- URISyntaxException {
- /*
- * this is somewhat brute-force but there doesn't seem to be another
- * way; we have to walk all private key files we find and try to open
- * them
- */
-
- PGPDigestCalculatorProvider calculatorProvider = new JcaPGPDigestCalculatorProviderBuilder()
- .build();
-
- PBEProtectionRemoverFactory passphraseProvider = new JcePBEProtectionRemoverFactory(
- passphrasePrompt.getPassphrase(publicKey.getFingerprint(),
- userKeyboxPath));
-
- try (Stream<Path> keyFiles = Files.walk(USER_SECRET_KEY_DIR)) {
- for (Path keyFile : keyFiles.filter(Files::isRegularFile)
- .collect(Collectors.toList())) {
- 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);
- }
- }
-
- passphrasePrompt.clear();
- return null;
- } catch (RuntimeException e) {
- passphrasePrompt.clear();
- throw e;
- } catch (IOException e) {
- passphrasePrompt.clear();
- throw new PGPException(MessageFormat.format(
- JGitText.get().gpgFailedToParseSecretKey,
- USER_SECRET_KEY_DIR.toAbsolutePath()), e);
- }
- }
-
- /**
- * Return the first suitable key for signing in the key ring collection. For
- * this case we only expect there to be one key available for signing.
- * </p>
- *
- * @param signingkey
- * @param secringFile
- *
- * @return the first suitable PGP secret key found for signing
- * @throws IOException
- * on I/O related errors
- * @throws PGPException
- * on BouncyCastle errors
- */
- private PGPSecretKey findSecretKeyInLegacySecring(String signingkey,
- Path secringFile) throws IOException, PGPException {
-
- try (InputStream in = newInputStream(secringFile)) {
- PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
- PGPUtil.getDecoderStream(new BufferedInputStream(in)),
- new JcaKeyFingerprintCalculator());
-
- String keyId = toFingerprint(signingkey).toLowerCase(Locale.ROOT);
- Iterator<PGPSecretKeyRing> keyrings = pgpSec.getKeyRings();
- while (keyrings.hasNext()) {
- PGPSecretKeyRing keyRing = keyrings.next();
- Iterator<PGPSecretKey> keys = keyRing.getSecretKeys();
- while (keys.hasNext()) {
- PGPSecretKey key = keys.next();
- // try key id
- String fingerprint = Hex
- .toHexString(key.getPublicKey().getFingerprint())
- .toLowerCase(Locale.ROOT);
- if (fingerprint.endsWith(keyId)) {
- return key;
- }
- // try user id
- Iterator<String> userIDs = key.getUserIDs();
- while (userIDs.hasNext()) {
- String userId = userIDs.next();
- if (containsSigningKey(userId, signingKey)) {
- return key;
- }
- }
- }
- }
- }
- return null;
- }
-
- /**
- * Return the first public key matching the key id ({@link #signingKey}.
- *
- * @param pubringFile
- *
- * @return the PGP public key, or {@code null} if none found
- * @throws IOException
- * on I/O related errors
- * @throws PGPException
- * on BouncyCastle errors
- */
- private PGPPublicKey findPublicKeyInPubring(Path pubringFile)
- throws IOException, PGPException {
- try (InputStream in = newInputStream(pubringFile)) {
- PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
- new BufferedInputStream(in),
- new JcaKeyFingerprintCalculator());
-
- String keyId = toFingerprint(signingKey).toLowerCase(Locale.ROOT);
- Iterator<PGPPublicKeyRing> keyrings = pgpPub.getKeyRings();
- while (keyrings.hasNext()) {
- PGPPublicKeyRing keyRing = keyrings.next();
- Iterator<PGPPublicKey> keys = keyRing.getPublicKeys();
- while (keys.hasNext()) {
- PGPPublicKey key = keys.next();
- // try key id
- String fingerprint = Hex.toHexString(key.getFingerprint())
- .toLowerCase(Locale.ROOT);
- if (fingerprint.endsWith(keyId)) {
- return key;
- }
- // try user id
- Iterator<String> userIDs = key.getUserIDs();
- while (userIDs.hasNext()) {
- String userId = userIDs.next();
- if (containsSigningKey(userId, signingKey)) {
- return key;
- }
- }
- }
- }
- }
- return null;
- }
-
- private PGPPublicKey getPublicKey(KeyBlob blob, byte[] fingerprint)
- throws IOException {
- return ((PublicKeyRingBlob) blob).getPGPPublicKeyRing()
- .getPublicKey(fingerprint);
- }
-
- private PGPPublicKey getSigningPublicKey(KeyBlob blob) throws IOException {
- PGPPublicKey masterKey = null;
- Iterator<PGPPublicKey> keys = ((PublicKeyRingBlob) blob)
- .getPGPPublicKeyRing().getPublicKeys();
- while (keys.hasNext()) {
- PGPPublicKey key = keys.next();
- // only consider keys that have the [S] usage flag set
- if (isSigningKey(key)) {
- if (key.isMasterKey()) {
- masterKey = key;
- } else {
- return key;
- }
- }
- }
- // return the master key if no other signing key was found or null if
- // the master key did not have the signing flag set
- return masterKey;
- }
-
- private boolean isSigningKey(PGPPublicKey key) {
- Iterator signatures = key.getSignatures();
- while (signatures.hasNext()) {
- PGPSignature sig = (PGPSignature) signatures.next();
- if ((sig.getHashedSubPackets().getKeyFlags()
- & PGPKeyFlags.CAN_SIGN) > 0) {
- return true;
- }
- }
- return false;
- }
-
- private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException,
- NoSuchAlgorithmException, NoSuchProviderException,
- NoOpenPgpKeyException {
- if (keyboxFile.toFile().length() == 0) {
- throw new NoOpenPgpKeyException();
- }
- KeyBox keyBox;
- try (InputStream in = new BufferedInputStream(
- newInputStream(keyboxFile))) {
- keyBox = new JcaKeyBoxBuilder().build(in);
- }
- return keyBox;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyPassphrasePrompt.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyPassphrasePrompt.java
deleted file mode 100644
index 6e29af51d8..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyPassphrasePrompt.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*-
- * Copyright (C) 2019, Salesforce. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.lib.internal;
-
-import java.net.URISyntaxException;
-import java.nio.file.Path;
-import java.text.MessageFormat;
-
-import org.bouncycastle.openpgp.PGPException;
-import org.bouncycastle.util.encoders.Hex;
-import org.eclipse.jgit.api.errors.CanceledException;
-import org.eclipse.jgit.errors.UnsupportedCredentialItem;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.transport.CredentialItem.CharArrayType;
-import org.eclipse.jgit.transport.CredentialItem.InformationalMessage;
-import org.eclipse.jgit.transport.CredentialsProvider;
-import org.eclipse.jgit.transport.URIish;
-
-/**
- * Prompts for a passphrase and caches it until {@link #clear() cleared}.
- * <p>
- * Implements {@link AutoCloseable} so it can be used within a
- * try-with-resources block.
- * </p>
- */
-class BouncyCastleGpgKeyPassphrasePrompt implements AutoCloseable {
-
- private CharArrayType passphrase;
-
- private CredentialsProvider credentialsProvider;
-
- public BouncyCastleGpgKeyPassphrasePrompt(
- CredentialsProvider credentialsProvider) {
- this.credentialsProvider = credentialsProvider;
- }
-
- /**
- * Clears any cached passphrase
- */
- public void clear() {
- if (passphrase != null) {
- passphrase.clear();
- passphrase = null;
- }
- }
-
- @Override
- public void close() {
- clear();
- }
-
- private URIish createURI(Path keyLocation) throws URISyntaxException {
- return new URIish(keyLocation.toUri().toString());
- }
-
- /**
- * Prompts use for a passphrase unless one was cached from a previous
- * prompt.
- *
- * @param keyFingerprint
- * the fingerprint to show to the user during prompting
- * @param keyLocation
- * the location the key was loaded from
- * @return the passphrase (maybe <code>null</code>)
- * @throws PGPException
- * @throws CanceledException
- * in case passphrase was not entered by user
- * @throws URISyntaxException
- * @throws UnsupportedCredentialItem
- */
- public char[] getPassphrase(byte[] keyFingerprint, Path keyLocation)
- throws PGPException, CanceledException, UnsupportedCredentialItem,
- URISyntaxException {
- if (passphrase == null) {
- passphrase = new CharArrayType(JGitText.get().credentialPassphrase,
- true);
- }
-
- if (credentialsProvider == null) {
- throw new PGPException(JGitText.get().gpgNoCredentialsProvider);
- }
-
- if (passphrase.getValue() == null
- && !credentialsProvider.get(createURI(keyLocation),
- new InformationalMessage(
- MessageFormat.format(JGitText.get().gpgKeyInfo,
- Hex.toHexString(keyFingerprint))),
- passphrase)) {
- throw new CanceledException(JGitText.get().gpgSigningCancelled);
- }
- return passphrase.getValue();
- }
-
-}
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
deleted file mode 100644
index 388169637e..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2018, Salesforce. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-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;
-import org.bouncycastle.bcpg.BCPGOutputStream;
-import org.bouncycastle.bcpg.HashAlgorithmTags;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openpgp.PGPException;
-import org.bouncycastle.openpgp.PGPPrivateKey;
-import org.bouncycastle.openpgp.PGPSecretKey;
-import org.bouncycastle.openpgp.PGPSignature;
-import org.bouncycastle.openpgp.PGPSignatureGenerator;
-import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
-import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
-import org.eclipse.jgit.annotations.NonNull;
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.api.errors.CanceledException;
-import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.errors.UnsupportedCredentialItem;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.GpgSignature;
-import org.eclipse.jgit.lib.GpgSigner;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.transport.CredentialsProvider;
-
-/**
- * GPG Signer using BouncyCastle library
- */
-public class BouncyCastleGpgSigner extends GpgSigner {
-
- private static void registerBouncyCastleProviderIfNecessary() {
- if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
- Security.addProvider(new BouncyCastleProvider());
- }
- }
-
- /**
- * Create a new instance.
- * <p>
- * The BounceCastleProvider will be registered if necessary.
- * </p>
- */
- public BouncyCastleGpgSigner() {
- registerBouncyCastleProviderIfNecessary();
- }
-
- @Override
- public boolean canLocateSigningKey(@Nullable String gpgSigningKey,
- PersonIdent committer, CredentialsProvider credentialsProvider)
- throws CanceledException {
- try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
- credentialsProvider)) {
- BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
- committer, passphrasePrompt);
- return gpgKey != null;
- } catch (PGPException | IOException | NoSuchAlgorithmException
- | NoSuchProviderException | URISyntaxException e) {
- return false;
- }
- }
-
- private BouncyCastleGpgKey locateSigningKey(@Nullable String gpgSigningKey,
- PersonIdent committer,
- BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt)
- throws CanceledException, UnsupportedCredentialItem, IOException,
- NoSuchAlgorithmException, NoSuchProviderException, PGPException,
- URISyntaxException {
- if (gpgSigningKey == null || gpgSigningKey.isEmpty()) {
- gpgSigningKey = '<' + committer.getEmailAddress() + '>';
- }
-
- BouncyCastleGpgKeyLocator keyHelper = new BouncyCastleGpgKeyLocator(
- gpgSigningKey, passphrasePrompt);
-
- return keyHelper.findSecretKey();
- }
-
- @Override
- public void sign(@NonNull CommitBuilder commit,
- @Nullable String gpgSigningKey, @NonNull PersonIdent committer,
- CredentialsProvider credentialsProvider) throws CanceledException {
- try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
- credentialsProvider)) {
- BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
- committer, passphrasePrompt);
- PGPSecretKey secretKey = gpgKey.getSecretKey();
- if (secretKey == null) {
- throw new JGitInternalException(
- JGitText.get().unableToSignCommitNoSecretKey);
- }
- char[] passphrase = passphrasePrompt.getPassphrase(
- secretKey.getPublicKey().getFingerprint(),
- gpgKey.getOrigin());
- PGPPrivateKey privateKey = secretKey
- .extractPrivateKey(new JcePBESecretKeyDecryptorBuilder()
- .setProvider(BouncyCastleProvider.PROVIDER_NAME)
- .build(passphrase));
- PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
- new JcaPGPContentSignerBuilder(
- secretKey.getPublicKey().getAlgorithm(),
- HashAlgorithmTags.SHA256).setProvider(
- BouncyCastleProvider.PROVIDER_NAME));
- signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, privateKey);
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- try (BCPGOutputStream out = new BCPGOutputStream(
- new ArmoredOutputStream(buffer))) {
- signatureGenerator.update(commit.build());
- signatureGenerator.generate().encode(out);
- }
- commit.setGpgSignature(new GpgSignature(buffer.toByteArray()));
- } catch (PGPException | IOException | NoSuchAlgorithmException
- | NoSuchProviderException | URISyntaxException e) {
- throw new JGitInternalException(e.getMessage(), e);
- }
- }
-}
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 6d4437f4c0..9b556393e2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java
@@ -70,4 +70,8 @@ class GlobalBundleCache {
throw new Error(e);
}
}
+
+ static void clear() {
+ cachedBundles.clear();
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java b/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
index daa039d347..d7dd3bee52 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
@@ -100,6 +100,15 @@ public class NLS {
return b.get(type);
}
+ /**
+ * Release resources held by NLS
+ * @since 5.8
+ */
+ public static void clear() {
+ local.remove();
+ GlobalBundleCache.clear();
+ }
+
private final Locale locale;
private final ConcurrentHashMap<Class, TranslationBundle> map = new ConcurrentHashMap<>();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapWalker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapWalker.java
index 023962e251..8cd5eb2238 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapWalker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapWalker.java
@@ -16,6 +16,7 @@ import java.util.Arrays;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.revwalk.AddToBitmapFilter;
+import org.eclipse.jgit.internal.revwalk.AddToBitmapWithCacheFilter;
import org.eclipse.jgit.internal.revwalk.AddUnseenToBitmapFilter;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.BitmapIndex;
@@ -41,6 +42,11 @@ public final class BitmapWalker {
private long countOfBitmapIndexMisses;
+ // Cached bitmap and commit to save walk time.
+ private AnyObjectId prevCommit;
+
+ private Bitmap prevBitmap;
+
/**
* Create a BitmapWalker.
*
@@ -56,6 +62,28 @@ public final class BitmapWalker {
}
/**
+ * Set the cached commit for the walker.
+ *
+ * @param prevCommit
+ * the cached commit.
+ * @since 5.8
+ */
+ public void setPrevCommit(AnyObjectId prevCommit) {
+ this.prevCommit = prevCommit;
+ }
+
+ /**
+ * Set the bitmap associated with the cached commit for the walker.
+ *
+ * @param prevBitmap
+ * the bitmap associated with the cached commit.
+ * @since 5.8
+ */
+ public void setPrevBitmap(Bitmap prevBitmap) {
+ this.prevBitmap = prevBitmap;
+ }
+
+ /**
* Return the number of objects that had to be walked because they were not covered by a
* bitmap.
*
@@ -169,7 +197,10 @@ public final class BitmapWalker {
}
if (marked) {
- if (seen == null) {
+ if (prevCommit != null) {
+ walker.setRevFilter(new AddToBitmapWithCacheFilter(prevCommit,
+ prevBitmap, bitmapResult));
+ } else if (seen == null) {
walker.setRevFilter(new AddToBitmapFilter(bitmapResult));
} else {
walker.setRevFilter(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
index 221353a91b..a12f652598 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
@@ -47,6 +47,8 @@ public class WindowCacheConfig {
private int streamFileThreshold;
+ private boolean exposeStats;
+
/**
* Create a default configuration.
*/
@@ -58,6 +60,7 @@ public class WindowCacheConfig {
packedGitMMAP = false;
deltaBaseCacheLimit = 10 * MB;
streamFileThreshold = PackConfig.DEFAULT_BIG_FILE_THRESHOLD;
+ exposeStats = true;
}
/**
@@ -220,6 +223,39 @@ public class WindowCacheConfig {
}
/**
+ * Tell whether the statistics JMX bean should be automatically registered.
+ * <p>
+ * Registration of that bean via JMX is additionally subject to a boolean
+ * JGit-specific user config "jmx.WindowCacheStats". The bean will be
+ * registered only if this user config is {@code true} <em>and</em>
+ * {@code getExposeStatsViaJmx() == true}.
+ * </p>
+ * <p>
+ * By default, this returns {@code true} unless changed via
+ * {@link #setExposeStatsViaJmx(boolean)}.
+ *
+ * @return whether to expose WindowCacheStats statistics via JMX upon
+ * {@link #install()}
+ * @since 5.8
+ */
+ public boolean getExposeStatsViaJmx() {
+ return exposeStats;
+ }
+
+ /**
+ * Defines whether the statistics JMX MBean should be automatically set up.
+ * (By default {@code true}.) If set to {@code false}, the JMX monitoring
+ * bean is not registered.
+ *
+ * @param expose
+ * whether to register the JMX Bean
+ * @since 5.8
+ */
+ public void setExposeStatsViaJmx(boolean expose) {
+ exposeStats = expose;
+ }
+
+ /**
* Update properties by setting fields from the configuration.
* <p>
* If a property is not defined in the configuration, then it is left
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java
deleted file mode 100644
index 10646b9e7a..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2010, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.transport;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import com.jcraft.jsch.Session;
-import com.jcraft.jsch.UIKeyboardInteractive;
-import com.jcraft.jsch.UserInfo;
-
-/**
- * A JSch {@link com.jcraft.jsch.UserInfo} adapter for a
- * {@link org.eclipse.jgit.transport.CredentialsProvider}.
- */
-public class CredentialsProviderUserInfo implements UserInfo,
- UIKeyboardInteractive {
- private final URIish uri;
-
- private final CredentialsProvider provider;
-
- private String password;
-
- private String passphrase;
-
- /**
- * Wrap a CredentialsProvider to make it suitable for use with JSch.
- *
- * @param session
- * the JSch session this UserInfo will support authentication on.
- * @param credentialsProvider
- * the provider that will perform the authentication.
- */
- public CredentialsProviderUserInfo(Session session,
- CredentialsProvider credentialsProvider) {
- this.uri = createURI(session);
- this.provider = credentialsProvider;
- }
-
- private static URIish createURI(Session session) {
- URIish uri = new URIish();
- uri = uri.setScheme("ssh"); //$NON-NLS-1$
- uri = uri.setUser(session.getUserName());
- uri = uri.setHost(session.getHost());
- uri = uri.setPort(session.getPort());
- return uri;
- }
-
- /** {@inheritDoc} */
- @Override
- public String getPassword() {
- return password;
- }
-
- /** {@inheritDoc} */
- @Override
- public String getPassphrase() {
- return passphrase;
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean promptPassphrase(String msg) {
- CredentialItem.StringType v = newPrompt(msg);
- if (provider.get(uri, v)) {
- passphrase = v.getValue();
- return true;
- }
- passphrase = null;
- return false;
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean promptPassword(String msg) {
- CredentialItem.Password p = new CredentialItem.Password(msg);
- if (provider.get(uri, p)) {
- password = new String(p.getValue());
- return true;
- }
- password = null;
- return false;
- }
-
- private CredentialItem.StringType newPrompt(String msg) {
- return new CredentialItem.StringType(msg, true);
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean promptYesNo(String msg) {
- CredentialItem.YesNoType v = new CredentialItem.YesNoType(msg);
- return provider.get(uri, v) && v.getValue();
- }
-
- /** {@inheritDoc} */
- @Override
- public void showMessage(String msg) {
- provider.get(uri, new CredentialItem.InformationalMessage(msg));
- }
-
- /** {@inheritDoc} */
- @Override
- public String[] promptKeyboardInteractive(String destination, String name,
- String instruction, String[] prompt, boolean[] echo) {
- CredentialItem.StringType[] v = new CredentialItem.StringType[prompt.length];
- for (int i = 0; i < prompt.length; i++)
- v[i] = new CredentialItem.StringType(prompt[i], !echo[i]);
-
- List<CredentialItem> items = new ArrayList<>();
- if (instruction != null && instruction.length() > 0)
- items.add(new CredentialItem.InformationalMessage(instruction));
- items.addAll(Arrays.asList(v));
-
- if (!provider.get(uri, items))
- return null; // cancel
-
- String[] result = new String[v.length];
- for (int i = 0; i < v.length; i++)
- result[i] = v[i].getValue();
- return result;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DefaultSshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DefaultSshSessionFactory.java
deleted file mode 100644
index afa0a11c24..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DefaultSshSessionFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * Copyright (C) 2009, Google Inc.
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.transport;
-
-import com.jcraft.jsch.Session;
-
-/**
- * Loads known hosts and private keys from <code>$HOME/.ssh</code>.
- * <p>
- * This is the default implementation used by JGit and provides most of the
- * compatibility necessary to match OpenSSH, a popular implementation of SSH
- * used by C Git.
- * <p>
- * If user interactivity is required by SSH (e.g. to obtain a password), the
- * connection will immediately fail.
- *
- * @since 5.7
- */
-public class DefaultSshSessionFactory extends JschConfigSessionFactory {
- /** {@inheritDoc} */
- @Override
- protected void configure(OpenSshConfig.Host hc, Session session) {
- // No additional configuration required.
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
deleted file mode 100644
index 718c8f6115..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
+++ /dev/null
@@ -1,530 +0,0 @@
-/*
- * Copyright (C) 2018, Sasa Zivkov <sasa.zivkov@sap.com>
- * Copyright (C) 2016, Mark Ingram <markdingram@gmail.com>
- * Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2009, Google, Inc.
- * Copyright (C) 2009, JetBrains s.r.o.
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.transport;
-
-import static java.util.stream.Collectors.joining;
-import static java.util.stream.Collectors.toList;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.ConnectException;
-import java.net.UnknownHostException;
-import java.text.MessageFormat;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Stream;
-
-import org.eclipse.jgit.errors.TransportException;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.util.FS;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.jcraft.jsch.ConfigRepository;
-import com.jcraft.jsch.ConfigRepository.Config;
-import com.jcraft.jsch.HostKey;
-import com.jcraft.jsch.HostKeyRepository;
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.Session;
-
-/**
- * The base session factory that loads known hosts and private keys from
- * <code>$HOME/.ssh</code>.
- * <p>
- * This is the default implementation used by JGit and provides most of the
- * compatibility necessary to match OpenSSH, a popular implementation of SSH
- * used by C Git.
- * <p>
- * The factory does not provide UI behavior. Override the method
- * {@link #configure(org.eclipse.jgit.transport.OpenSshConfig.Host, Session)} to
- * supply appropriate {@link com.jcraft.jsch.UserInfo} to the session.
- */
-public abstract class JschConfigSessionFactory extends SshSessionFactory {
-
- private static final Logger LOG = LoggerFactory
- .getLogger(JschConfigSessionFactory.class);
-
- /**
- * We use different Jsch instances for hosts that have an IdentityFile
- * configured in ~/.ssh/config. Jsch by default would cache decrypted keys
- * only per session, which results in repeated password prompts. Using
- * different Jsch instances, we can cache the keys on these instances so
- * that they will be re-used for successive sessions, and thus the user is
- * prompted for a key password only once while Eclipse runs.
- */
- private final Map<String, JSch> byIdentityFile = new HashMap<>();
-
- private JSch defaultJSch;
-
- private OpenSshConfig config;
-
- /** {@inheritDoc} */
- @Override
- public synchronized RemoteSession getSession(URIish uri,
- CredentialsProvider credentialsProvider, FS fs, int tms)
- throws TransportException {
-
- String user = uri.getUser();
- final String pass = uri.getPass();
- String host = uri.getHost();
- int port = uri.getPort();
-
- try {
- if (config == null)
- config = OpenSshConfig.get(fs);
-
- final OpenSshConfig.Host hc = config.lookup(host);
- if (port <= 0)
- port = hc.getPort();
- if (user == null)
- user = hc.getUser();
-
- Session session = createSession(credentialsProvider, fs, user,
- pass, host, port, hc);
-
- int retries = 0;
- while (!session.isConnected()) {
- try {
- retries++;
- session.connect(tms);
- } catch (JSchException e) {
- session.disconnect();
- session = null;
- // Make sure our known_hosts is not outdated
- knownHosts(getJSch(hc, fs), fs);
-
- if (isAuthenticationCanceled(e)) {
- throw e;
- } else if (isAuthenticationFailed(e)
- && credentialsProvider != null) {
- // if authentication failed maybe credentials changed at
- // the remote end therefore reset credentials and retry
- if (retries < 3) {
- credentialsProvider.reset(uri);
- session = createSession(credentialsProvider, fs,
- user, pass, host, port, hc);
- } else
- throw e;
- } else if (retries >= hc.getConnectionAttempts()) {
- throw e;
- } else {
- try {
- Thread.sleep(1000);
- session = createSession(credentialsProvider, fs,
- user, pass, host, port, hc);
- } catch (InterruptedException e1) {
- throw new TransportException(
- JGitText.get().transportSSHRetryInterrupt,
- e1);
- }
- }
- }
- }
-
- return new JschSession(session, uri);
-
- } catch (JSchException je) {
- final Throwable c = je.getCause();
- if (c instanceof UnknownHostException) {
- throw new TransportException(uri, JGitText.get().unknownHost,
- je);
- }
- if (c instanceof ConnectException) {
- throw new TransportException(uri, c.getMessage(), je);
- }
- throw new TransportException(uri, je.getMessage(), je);
- }
-
- }
-
- private static boolean isAuthenticationFailed(JSchException e) {
- return e.getCause() == null && e.getMessage().equals("Auth fail"); //$NON-NLS-1$
- }
-
- private static boolean isAuthenticationCanceled(JSchException e) {
- return e.getCause() == null && e.getMessage().equals("Auth cancel"); //$NON-NLS-1$
- }
-
- // Package visibility for tests
- Session createSession(CredentialsProvider credentialsProvider,
- FS fs, String user, final String pass, String host, int port,
- final OpenSshConfig.Host hc) throws JSchException {
- final Session session = createSession(hc, user, host, port, fs);
- // Jsch will have overridden the explicit user by the one from the SSH
- // config file...
- setUserName(session, user);
- // Jsch will also have overridden the port.
- if (port > 0 && port != session.getPort()) {
- session.setPort(port);
- }
- // We retry already in getSession() method. JSch must not retry
- // on its own.
- session.setConfig("MaxAuthTries", "1"); //$NON-NLS-1$ //$NON-NLS-2$
- if (pass != null)
- session.setPassword(pass);
- final String strictHostKeyCheckingPolicy = hc
- .getStrictHostKeyChecking();
- if (strictHostKeyCheckingPolicy != null)
- session.setConfig("StrictHostKeyChecking", //$NON-NLS-1$
- strictHostKeyCheckingPolicy);
- final String pauth = hc.getPreferredAuthentications();
- if (pauth != null)
- session.setConfig("PreferredAuthentications", pauth); //$NON-NLS-1$
- if (credentialsProvider != null
- && (!hc.isBatchMode() || !credentialsProvider.isInteractive())) {
- session.setUserInfo(new CredentialsProviderUserInfo(session,
- credentialsProvider));
- }
- safeConfig(session, hc.getConfig());
- if (hc.getConfig().getValue("HostKeyAlgorithms") == null) { //$NON-NLS-1$
- setPreferredKeyTypesOrder(session);
- }
- configure(hc, session);
- return session;
- }
-
- private void safeConfig(Session session, Config cfg) {
- // Ensure that Jsch checks all configured algorithms, not just its
- // built-in ones. Otherwise it may propose an algorithm for which it
- // doesn't have an implementation, and then run into an NPE if that
- // algorithm ends up being chosen.
- copyConfigValueToSession(session, cfg, "Ciphers", "CheckCiphers"); //$NON-NLS-1$ //$NON-NLS-2$
- copyConfigValueToSession(session, cfg, "KexAlgorithms", "CheckKexes"); //$NON-NLS-1$ //$NON-NLS-2$
- copyConfigValueToSession(session, cfg, "HostKeyAlgorithms", //$NON-NLS-1$
- "CheckSignatures"); //$NON-NLS-1$
- }
-
- private static void setPreferredKeyTypesOrder(Session session) {
- HostKeyRepository hkr = session.getHostKeyRepository();
- HostKey[] hostKeys = hkr.getHostKey(hostName(session), null);
-
- if (hostKeys == null) {
- return;
- }
-
- List<String> known = Stream.of(hostKeys)
- .map(HostKey::getType)
- .collect(toList());
-
- if (!known.isEmpty()) {
- String serverHostKey = "server_host_key"; //$NON-NLS-1$
- String current = session.getConfig(serverHostKey);
- if (current == null) {
- session.setConfig(serverHostKey, String.join(",", known)); //$NON-NLS-1$
- return;
- }
-
- String knownFirst = Stream.concat(
- known.stream(),
- Stream.of(current.split(",")) //$NON-NLS-1$
- .filter(s -> !known.contains(s)))
- .collect(joining(",")); //$NON-NLS-1$
- session.setConfig(serverHostKey, knownFirst);
- }
- }
-
- private static String hostName(Session s) {
- if (s.getPort() == SshConstants.SSH_DEFAULT_PORT) {
- return s.getHost();
- }
- return String.format("[%s]:%d", s.getHost(), //$NON-NLS-1$
- Integer.valueOf(s.getPort()));
- }
-
- private void copyConfigValueToSession(Session session, Config cfg,
- String from, String to) {
- String value = cfg.getValue(from);
- if (value != null) {
- session.setConfig(to, value);
- }
- }
-
- private void setUserName(Session session, String userName) {
- // Jsch 0.1.54 picks up the user name from the ssh config, even if an
- // explicit user name was given! We must correct that if ~/.ssh/config
- // has a different user name.
- if (userName == null || userName.isEmpty()
- || userName.equals(session.getUserName())) {
- return;
- }
- try {
- Class<?>[] parameterTypes = { String.class };
- Method method = Session.class.getDeclaredMethod("setUserName", //$NON-NLS-1$
- parameterTypes);
- method.setAccessible(true);
- method.invoke(session, userName);
- } catch (NullPointerException | IllegalAccessException
- | IllegalArgumentException | InvocationTargetException
- | NoSuchMethodException | SecurityException e) {
- LOG.error(MessageFormat.format(JGitText.get().sshUserNameError,
- userName, session.getUserName()), e);
- }
- }
-
- /**
- * Create a new remote session for the requested address.
- *
- * @param hc
- * host configuration
- * @param user
- * login to authenticate as.
- * @param host
- * server name to connect to.
- * @param port
- * port number of the SSH daemon (typically 22).
- * @param fs
- * the file system abstraction which will be necessary to
- * perform certain file system operations.
- * @return new session instance, but otherwise unconfigured.
- * @throws com.jcraft.jsch.JSchException
- * the session could not be created.
- */
- protected Session createSession(final OpenSshConfig.Host hc,
- final String user, final String host, final int port, FS fs)
- throws JSchException {
- return getJSch(hc, fs).getSession(user, host, port);
- }
-
- /**
- * Provide additional configuration for the JSch instance. This method could
- * be overridden to supply a preferred
- * {@link com.jcraft.jsch.IdentityRepository}.
- *
- * @param jsch
- * jsch instance
- * @since 4.5
- */
- protected void configureJSch(JSch jsch) {
- // No additional configuration required.
- }
-
- /**
- * Provide additional configuration for the session based on the host
- * information. This method could be used to supply
- * {@link com.jcraft.jsch.UserInfo}.
- *
- * @param hc
- * host configuration
- * @param session
- * session to configure
- */
- protected abstract void configure(OpenSshConfig.Host hc, Session session);
-
- /**
- * Obtain the JSch used to create new sessions.
- *
- * @param hc
- * host configuration
- * @param fs
- * the file system abstraction which will be necessary to
- * perform certain file system operations.
- * @return the JSch instance to use.
- * @throws com.jcraft.jsch.JSchException
- * the user configuration could not be created.
- */
- protected JSch getJSch(OpenSshConfig.Host hc, FS fs) throws JSchException {
- if (defaultJSch == null) {
- defaultJSch = createDefaultJSch(fs);
- if (defaultJSch.getConfigRepository() == null) {
- defaultJSch.setConfigRepository(
- new JschBugFixingConfigRepository(config));
- }
- for (Object name : defaultJSch.getIdentityNames())
- byIdentityFile.put((String) name, defaultJSch);
- }
-
- final File identityFile = hc.getIdentityFile();
- if (identityFile == null)
- return defaultJSch;
-
- final String identityKey = identityFile.getAbsolutePath();
- JSch jsch = byIdentityFile.get(identityKey);
- if (jsch == null) {
- jsch = new JSch();
- configureJSch(jsch);
- if (jsch.getConfigRepository() == null) {
- jsch.setConfigRepository(defaultJSch.getConfigRepository());
- }
- jsch.setHostKeyRepository(defaultJSch.getHostKeyRepository());
- jsch.addIdentity(identityKey);
- byIdentityFile.put(identityKey, jsch);
- }
- return jsch;
- }
-
- /**
- * Create default instance of jsch
- *
- * @param fs
- * the file system abstraction which will be necessary to perform
- * certain file system operations.
- * @return the new default JSch implementation.
- * @throws com.jcraft.jsch.JSchException
- * known host keys cannot be loaded.
- */
- protected JSch createDefaultJSch(FS fs) throws JSchException {
- final JSch jsch = new JSch();
- JSch.setConfig("ssh-rsa", JSch.getConfig("signature.rsa")); //$NON-NLS-1$ //$NON-NLS-2$
- JSch.setConfig("ssh-dss", JSch.getConfig("signature.dss")); //$NON-NLS-1$ //$NON-NLS-2$
- configureJSch(jsch);
- knownHosts(jsch, fs);
- identities(jsch, fs);
- return jsch;
- }
-
- private static void knownHosts(JSch sch, FS fs) throws JSchException {
- final File home = fs.userHome();
- if (home == null)
- return;
- final File known_hosts = new File(new File(home, ".ssh"), "known_hosts"); //$NON-NLS-1$ //$NON-NLS-2$
- try (FileInputStream in = new FileInputStream(known_hosts)) {
- sch.setKnownHosts(in);
- } catch (FileNotFoundException none) {
- // Oh well. They don't have a known hosts in home.
- } catch (IOException err) {
- // Oh well. They don't have a known hosts in home.
- }
- }
-
- private static void identities(JSch sch, FS fs) {
- final File home = fs.userHome();
- if (home == null)
- return;
- final File sshdir = new File(home, ".ssh"); //$NON-NLS-1$
- if (sshdir.isDirectory()) {
- loadIdentity(sch, new File(sshdir, "identity")); //$NON-NLS-1$
- loadIdentity(sch, new File(sshdir, "id_rsa")); //$NON-NLS-1$
- loadIdentity(sch, new File(sshdir, "id_dsa")); //$NON-NLS-1$
- }
- }
-
- private static void loadIdentity(JSch sch, File priv) {
- if (priv.isFile()) {
- try {
- sch.addIdentity(priv.getAbsolutePath());
- } catch (JSchException e) {
- // Instead, pretend the key doesn't exist.
- }
- }
- }
-
- private static class JschBugFixingConfigRepository
- implements ConfigRepository {
-
- private final ConfigRepository base;
-
- public JschBugFixingConfigRepository(ConfigRepository base) {
- this.base = base;
- }
-
- @Override
- public Config getConfig(String host) {
- return new JschBugFixingConfig(base.getConfig(host));
- }
-
- /**
- * A {@link com.jcraft.jsch.ConfigRepository.Config} that transforms
- * some values from the config file into the format Jsch 0.1.54 expects.
- * This is a work-around for bugs in Jsch.
- * <p>
- * Additionally, this config hides the IdentityFile config entries from
- * Jsch; we manage those ourselves. Otherwise Jsch would cache passwords
- * (or rather, decrypted keys) only for a single session, resulting in
- * multiple password prompts for user operations that use several Jsch
- * sessions.
- */
- private static class JschBugFixingConfig implements Config {
-
- private static final String[] NO_IDENTITIES = {};
-
- private final Config real;
-
- public JschBugFixingConfig(Config delegate) {
- real = delegate;
- }
-
- @Override
- public String getHostname() {
- return real.getHostname();
- }
-
- @Override
- public String getUser() {
- return real.getUser();
- }
-
- @Override
- public int getPort() {
- return real.getPort();
- }
-
- @Override
- public String getValue(String key) {
- String k = key.toUpperCase(Locale.ROOT);
- if ("IDENTITYFILE".equals(k)) { //$NON-NLS-1$
- return null;
- }
- String result = real.getValue(key);
- if (result != null) {
- if ("SERVERALIVEINTERVAL".equals(k) //$NON-NLS-1$
- || "CONNECTTIMEOUT".equals(k)) { //$NON-NLS-1$
- // These values are in seconds. Jsch 0.1.54 passes them
- // on as is to java.net.Socket.setSoTimeout(), which
- // expects milliseconds. So convert here to
- // milliseconds.
- try {
- int timeout = Integer.parseInt(result);
- result = Long.toString(
- TimeUnit.SECONDS.toMillis(timeout));
- } catch (NumberFormatException e) {
- // Ignore
- }
- }
- }
- return result;
- }
-
- @Override
- public String[] getValues(String key) {
- String k = key.toUpperCase(Locale.ROOT);
- if ("IDENTITYFILE".equals(k)) { //$NON-NLS-1$
- return NO_IDENTITIES;
- }
- return real.getValues(key);
- }
- }
- }
-
- /**
- * Set the {@link OpenSshConfig} to use. Intended for use in tests.
- *
- * @param config
- * to use
- */
- synchronized void setConfig(OpenSshConfig config) {
- this.config = config;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
deleted file mode 100644
index d7270343cb..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2009, Google, Inc.
- * Copyright (C) 2009, JetBrains s.r.o.
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.transport;
-
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jgit.errors.TransportException;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.util.io.IsolatedOutputStream;
-
-import com.jcraft.jsch.Channel;
-import com.jcraft.jsch.ChannelExec;
-import com.jcraft.jsch.ChannelSftp;
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.Session;
-import com.jcraft.jsch.SftpException;
-
-/**
- * Run remote commands using Jsch.
- * <p>
- * This class is the default session implementation using Jsch. Note that
- * {@link org.eclipse.jgit.transport.JschConfigSessionFactory} is used to create
- * the actual session passed to the constructor.
- */
-public class JschSession implements RemoteSession {
- final Session sock;
- final URIish uri;
-
- /**
- * Create a new session object by passing the real Jsch session and the URI
- * information.
- *
- * @param session
- * the real Jsch session created elsewhere.
- * @param uri
- * the URI information for the remote connection
- */
- public JschSession(Session session, URIish uri) {
- sock = session;
- this.uri = uri;
- }
-
- /** {@inheritDoc} */
- @Override
- public Process exec(String command, int timeout) throws IOException {
- return new JschProcess(command, timeout);
- }
-
- /** {@inheritDoc} */
- @Override
- public void disconnect() {
- if (sock.isConnected())
- sock.disconnect();
- }
-
- /**
- * A kludge to allow {@link org.eclipse.jgit.transport.TransportSftp} to get
- * an Sftp channel from Jsch. Ideally, this method would be generic, which
- * would require implementing generic Sftp channel operations in the
- * RemoteSession class.
- *
- * @return a channel suitable for Sftp operations.
- * @throws com.jcraft.jsch.JSchException
- * on problems getting the channel.
- * @deprecated since 5.2; use {@link #getFtpChannel()} instead
- */
- @Deprecated
- public Channel getSftpChannel() throws JSchException {
- return sock.openChannel("sftp"); //$NON-NLS-1$
- }
-
- /**
- * {@inheritDoc}
- *
- * @since 5.2
- */
- @Override
- public FtpChannel getFtpChannel() {
- return new JschFtpChannel();
- }
-
- /**
- * Implementation of Process for running a single command using Jsch.
- * <p>
- * Uses the Jsch session to do actual command execution and manage the
- * execution.
- */
- private class JschProcess extends Process {
- private ChannelExec channel;
-
- final int timeout;
-
- private InputStream inputStream;
-
- private OutputStream outputStream;
-
- private InputStream errStream;
-
- /**
- * Opens a channel on the session ("sock") for executing the given
- * command, opens streams, and starts command execution.
- *
- * @param commandName
- * the command to execute
- * @param tms
- * the timeout value, in seconds, for the command.
- * @throws TransportException
- * on problems opening a channel or connecting to the remote
- * host
- * @throws IOException
- * on problems opening streams
- */
- JschProcess(String commandName, int tms)
- throws TransportException, IOException {
- timeout = tms;
- try {
- channel = (ChannelExec) sock.openChannel("exec"); //$NON-NLS-1$
- channel.setCommand(commandName);
- setupStreams();
- channel.connect(timeout > 0 ? timeout * 1000 : 0);
- if (!channel.isConnected()) {
- closeOutputStream();
- throw new TransportException(uri,
- JGitText.get().connectionFailed);
- }
- } catch (JSchException e) {
- closeOutputStream();
- throw new TransportException(uri, e.getMessage(), e);
- }
- }
-
- private void closeOutputStream() {
- if (outputStream != null) {
- try {
- outputStream.close();
- } catch (IOException ioe) {
- // ignore
- }
- }
- }
-
- private void setupStreams() throws IOException {
- inputStream = channel.getInputStream();
-
- // JSch won't let us interrupt writes when we use our InterruptTimer
- // to break out of a long-running write operation. To work around
- // that we spawn a background thread to shuttle data through a pipe,
- // as we can issue an interrupted write out of that. Its slower, so
- // we only use this route if there is a timeout.
- OutputStream out = channel.getOutputStream();
- if (timeout <= 0) {
- outputStream = out;
- } else {
- IsolatedOutputStream i = new IsolatedOutputStream(out);
- outputStream = new BufferedOutputStream(i, 16 * 1024);
- }
-
- errStream = channel.getErrStream();
- }
-
- @Override
- public InputStream getInputStream() {
- return inputStream;
- }
-
- @Override
- public OutputStream getOutputStream() {
- return outputStream;
- }
-
- @Override
- public InputStream getErrorStream() {
- return errStream;
- }
-
- @Override
- public int exitValue() {
- if (isRunning())
- throw new IllegalStateException();
- return channel.getExitStatus();
- }
-
- private boolean isRunning() {
- return channel.getExitStatus() < 0 && channel.isConnected();
- }
-
- @Override
- public void destroy() {
- if (channel.isConnected())
- channel.disconnect();
- closeOutputStream();
- }
-
- @Override
- public int waitFor() throws InterruptedException {
- while (isRunning())
- Thread.sleep(100);
- return exitValue();
- }
- }
-
- private class JschFtpChannel implements FtpChannel {
-
- private ChannelSftp ftp;
-
- @Override
- public void connect(int timeout, TimeUnit unit) throws IOException {
- try {
- ftp = (ChannelSftp) sock.openChannel("sftp"); //$NON-NLS-1$
- ftp.connect((int) unit.toMillis(timeout));
- } catch (JSchException e) {
- ftp = null;
- throw new IOException(e.getLocalizedMessage(), e);
- }
- }
-
- @Override
- public void disconnect() {
- ftp.disconnect();
- ftp = null;
- }
-
- private <T> T map(Callable<T> op) throws IOException {
- try {
- return op.call();
- } catch (Exception e) {
- if (e instanceof SftpException) {
- throw new FtpChannel.FtpException(e.getLocalizedMessage(),
- ((SftpException) e).id, e);
- }
- throw new IOException(e.getLocalizedMessage(), e);
- }
- }
-
- @Override
- public boolean isConnected() {
- return ftp != null && sock.isConnected();
- }
-
- @Override
- public void cd(String path) throws IOException {
- map(() -> {
- ftp.cd(path);
- return null;
- });
- }
-
- @Override
- public String pwd() throws IOException {
- return map(() -> ftp.pwd());
- }
-
- @Override
- public Collection<DirEntry> ls(String path) throws IOException {
- return map(() -> {
- List<DirEntry> result = new ArrayList<>();
- for (Object e : ftp.ls(path)) {
- ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) e;
- result.add(new DirEntry() {
-
- @Override
- public String getFilename() {
- return entry.getFilename();
- }
-
- @Override
- public long getModifiedTime() {
- return entry.getAttrs().getMTime();
- }
-
- @Override
- public boolean isDirectory() {
- return entry.getAttrs().isDir();
- }
- });
- }
- return result;
- });
- }
-
- @Override
- public void rmdir(String path) throws IOException {
- map(() -> {
- ftp.rm(path);
- return null;
- });
- }
-
- @Override
- public void mkdir(String path) throws IOException {
- map(() -> {
- ftp.mkdir(path);
- return null;
- });
- }
-
- @Override
- public InputStream get(String path) throws IOException {
- return map(() -> ftp.get(path));
- }
-
- @Override
- public OutputStream put(String path) throws IOException {
- return map(() -> ftp.put(path));
- }
-
- @Override
- public void rm(String path) throws IOException {
- map(() -> {
- ftp.rm(path);
- return null;
- });
- }
-
- @Override
- public void rename(String from, String to) throws IOException {
- map(() -> {
- // Plain FTP rename will fail if "to" exists. Jsch knows about
- // the FTP extension "posix-rename@openssh.com", which will
- // remove "to" first if it exists.
- if (hasPosixRename()) {
- ftp.rename(from, to);
- } else if (!to.equals(from)) {
- // Try to remove "to" first. With git, we typically get this
- // when a lock file is moved over the file locked. Note that
- // the check for to being equal to from may still fail in
- // the general case, but for use with JGit's TransportSftp
- // it should be good enough.
- delete(to);
- ftp.rename(from, to);
- }
- return null;
- });
- }
-
- /**
- * Determine whether the server has the posix-rename extension.
- *
- * @return {@code true} if it is supported, {@code false} otherwise
- * @see <a href=
- * "https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL?annotate=HEAD">OpenSSH
- * deviations and extensions to the published SSH protocol</a>
- * @see <a href=
- * "http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html">stdio.h:
- * rename()</a>
- */
- private boolean hasPosixRename() {
- return "1".equals(ftp.getExtension("posix-rename@openssh.com")); //$NON-NLS-1$//$NON-NLS-2$
- }
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java
deleted file mode 100644
index a628897a59..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright (C) 2008, 2018, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.transport;
-
-import static org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.positive;
-
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile;
-import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.HostEntry;
-import org.eclipse.jgit.util.FS;
-
-import com.jcraft.jsch.ConfigRepository;
-
-/**
- * Fairly complete configuration parser for the OpenSSH ~/.ssh/config file.
- * <p>
- * JSch does have its own config file parser
- * {@link com.jcraft.jsch.OpenSSHConfig} since version 0.1.50, but it has a
- * number of problems:
- * <ul>
- * <li>it splits lines of the format "keyword = value" wrongly: you'd end up
- * with the value "= value".
- * <li>its "Host" keyword is not case insensitive.
- * <li>it doesn't handle quoted values.
- * <li>JSch's OpenSSHConfig doesn't monitor for config file changes.
- * </ul>
- * <p>
- * This parser makes the critical options available to
- * {@link org.eclipse.jgit.transport.SshSessionFactory} via
- * {@link org.eclipse.jgit.transport.OpenSshConfig.Host} objects returned by
- * {@link #lookup(String)}, and implements a fully conforming
- * {@link com.jcraft.jsch.ConfigRepository} providing
- * {@link com.jcraft.jsch.ConfigRepository.Config}s via
- * {@link #getConfig(String)}.
- * </p>
- *
- * @see OpenSshConfigFile
- */
-public class OpenSshConfig implements ConfigRepository {
-
- /**
- * Obtain the user's configuration data.
- * <p>
- * The configuration file is always returned to the caller, even if no file
- * exists in the user's home directory at the time the call was made. Lookup
- * requests are cached and are automatically updated if the user modifies
- * the configuration file since the last time it was cached.
- *
- * @param fs
- * the file system abstraction which will be necessary to
- * perform certain file system operations.
- * @return a caching reader of the user's configuration file.
- */
- public static OpenSshConfig get(FS fs) {
- File home = fs.userHome();
- if (home == null)
- home = new File(".").getAbsoluteFile(); //$NON-NLS-1$
-
- final File config = new File(new File(home, SshConstants.SSH_DIR),
- SshConstants.CONFIG);
- return new OpenSshConfig(home, config);
- }
-
- /** The base file. */
- private OpenSshConfigFile configFile;
-
- OpenSshConfig(File h, File cfg) {
- configFile = new OpenSshConfigFile(h, cfg,
- SshSessionFactory.getLocalUserName());
- }
-
- /**
- * Locate the configuration for a specific host request.
- *
- * @param hostName
- * the name the user has supplied to the SSH tool. This may be a
- * real host name, or it may just be a "Host" block in the
- * configuration file.
- * @return r configuration for the requested name. Never null.
- */
- public Host lookup(String hostName) {
- HostEntry entry = configFile.lookup(hostName, -1, null);
- return new Host(entry, hostName, configFile.getLocalUserName());
- }
-
- /**
- * Configuration of one "Host" block in the configuration file.
- * <p>
- * If returned from {@link OpenSshConfig#lookup(String)} some or all of the
- * properties may not be populated. The properties which are not populated
- * should be defaulted by the caller.
- * <p>
- * When returned from {@link OpenSshConfig#lookup(String)} any wildcard
- * entries which appear later in the configuration file will have been
- * already merged into this block.
- */
- public static class Host {
- String hostName;
-
- int port;
-
- File identityFile;
-
- String user;
-
- String preferredAuthentications;
-
- Boolean batchMode;
-
- String strictHostKeyChecking;
-
- int connectionAttempts;
-
- private HostEntry entry;
-
- private Config config;
-
- // See com.jcraft.jsch.OpenSSHConfig. Translates some command-line keys
- // to ssh-config keys.
- private static final Map<String, String> KEY_MAP = new TreeMap<>(
- String.CASE_INSENSITIVE_ORDER);
-
- static {
- KEY_MAP.put("kex", SshConstants.KEX_ALGORITHMS); //$NON-NLS-1$
- KEY_MAP.put("server_host_key", SshConstants.HOST_KEY_ALGORITHMS); //$NON-NLS-1$
- KEY_MAP.put("cipher.c2s", SshConstants.CIPHERS); //$NON-NLS-1$
- KEY_MAP.put("cipher.s2c", SshConstants.CIPHERS); //$NON-NLS-1$
- KEY_MAP.put("mac.c2s", SshConstants.MACS); //$NON-NLS-1$
- KEY_MAP.put("mac.s2c", SshConstants.MACS); //$NON-NLS-1$
- KEY_MAP.put("compression.s2c", SshConstants.COMPRESSION); //$NON-NLS-1$
- KEY_MAP.put("compression.c2s", SshConstants.COMPRESSION); //$NON-NLS-1$
- KEY_MAP.put("compression_level", "CompressionLevel"); //$NON-NLS-1$ //$NON-NLS-2$
- KEY_MAP.put("MaxAuthTries", //$NON-NLS-1$
- SshConstants.NUMBER_OF_PASSWORD_PROMPTS);
- }
-
- private static String mapKey(String key) {
- String k = KEY_MAP.get(key);
- return k != null ? k : key;
- }
-
- /**
- * Creates a new uninitialized {@link Host}.
- */
- public Host() {
- // For API backwards compatibility with pre-4.9 JGit
- }
-
- Host(HostEntry entry, String hostName, String localUserName) {
- this.entry = entry;
- complete(hostName, localUserName);
- }
-
- /**
- * @return the value StrictHostKeyChecking property, the valid values
- * are "yes" (unknown hosts are not accepted), "no" (unknown
- * hosts are always accepted), and "ask" (user should be asked
- * before accepting the host)
- */
- public String getStrictHostKeyChecking() {
- return strictHostKeyChecking;
- }
-
- /**
- * @return the real IP address or host name to connect to; never null.
- */
- public String getHostName() {
- return hostName;
- }
-
- /**
- * @return the real port number to connect to; never 0.
- */
- public int getPort() {
- return port;
- }
-
- /**
- * @return path of the private key file to use for authentication; null
- * if the caller should use default authentication strategies.
- */
- public File getIdentityFile() {
- return identityFile;
- }
-
- /**
- * @return the real user name to connect as; never null.
- */
- public String getUser() {
- return user;
- }
-
- /**
- * @return the preferred authentication methods, separated by commas if
- * more than one authentication method is preferred.
- */
- public String getPreferredAuthentications() {
- return preferredAuthentications;
- }
-
- /**
- * @return true if batch (non-interactive) mode is preferred for this
- * host connection.
- */
- public boolean isBatchMode() {
- return batchMode != null && batchMode.booleanValue();
- }
-
- /**
- * @return the number of tries (one per second) to connect before
- * exiting. The argument must be an integer. This may be useful
- * in scripts if the connection sometimes fails. The default is
- * 1.
- * @since 3.4
- */
- public int getConnectionAttempts() {
- return connectionAttempts;
- }
-
-
- private void complete(String initialHostName, String localUserName) {
- // Try to set values from the options.
- hostName = entry.getValue(SshConstants.HOST_NAME);
- user = entry.getValue(SshConstants.USER);
- port = positive(entry.getValue(SshConstants.PORT));
- connectionAttempts = positive(
- entry.getValue(SshConstants.CONNECTION_ATTEMPTS));
- strictHostKeyChecking = entry
- .getValue(SshConstants.STRICT_HOST_KEY_CHECKING);
- batchMode = Boolean.valueOf(OpenSshConfigFile
- .flag(entry.getValue(SshConstants.BATCH_MODE)));
- preferredAuthentications = entry
- .getValue(SshConstants.PREFERRED_AUTHENTICATIONS);
- // Fill in defaults if still not set
- if (hostName == null || hostName.isEmpty()) {
- hostName = initialHostName;
- }
- if (user == null || user.isEmpty()) {
- user = localUserName;
- }
- if (port <= 0) {
- port = SshConstants.SSH_DEFAULT_PORT;
- }
- if (connectionAttempts <= 0) {
- connectionAttempts = 1;
- }
- List<String> identityFiles = entry
- .getValues(SshConstants.IDENTITY_FILE);
- if (identityFiles != null && !identityFiles.isEmpty()) {
- identityFile = new File(identityFiles.get(0));
- }
- }
-
- Config getConfig() {
- if (config == null) {
- config = new Config() {
-
- @Override
- public String getHostname() {
- return Host.this.getHostName();
- }
-
- @Override
- public String getUser() {
- return Host.this.getUser();
- }
-
- @Override
- public int getPort() {
- return Host.this.getPort();
- }
-
- @Override
- public String getValue(String key) {
- // See com.jcraft.jsch.OpenSSHConfig.MyConfig.getValue()
- // for this special case.
- if (key.equals("compression.s2c") //$NON-NLS-1$
- || key.equals("compression.c2s")) { //$NON-NLS-1$
- if (!OpenSshConfigFile.flag(
- Host.this.entry.getValue(mapKey(key)))) {
- return "none,zlib@openssh.com,zlib"; //$NON-NLS-1$
- }
- return "zlib@openssh.com,zlib,none"; //$NON-NLS-1$
- }
- return Host.this.entry.getValue(mapKey(key));
- }
-
- @Override
- public String[] getValues(String key) {
- List<String> values = Host.this.entry
- .getValues(mapKey(key));
- if (values == null) {
- return new String[0];
- }
- return values.toArray(new String[0]);
- }
- };
- }
- return config;
- }
-
- @Override
- @SuppressWarnings("nls")
- public String toString() {
- return "Host [hostName=" + hostName + ", port=" + port
- + ", identityFile=" + identityFile + ", user=" + user
- + ", preferredAuthentications=" + preferredAuthentications
- + ", batchMode=" + batchMode + ", strictHostKeyChecking="
- + strictHostKeyChecking + ", connectionAttempts="
- + connectionAttempts + ", entry=" + entry + "]";
- }
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Retrieves the full {@link com.jcraft.jsch.ConfigRepository.Config Config}
- * for the given host name. Should be called only by Jsch and tests.
- *
- * @since 4.9
- */
- @Override
- public Config getConfig(String hostName) {
- Host host = lookup(hostName);
- return host.getConfig();
- }
-
- /** {@inheritDoc} */
- @Override
- public String toString() {
- return "OpenSshConfig [configFile=" + configFile + ']'; //$NON-NLS-1$
- }
-}
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 8a8c1ae0ba..79f60c3202 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -48,6 +48,7 @@ import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.PackLock;
import org.eclipse.jgit.internal.submodule.SubmoduleValidator;
import org.eclipse.jgit.internal.submodule.SubmoduleValidator.SubmoduleValidationException;
+import org.eclipse.jgit.internal.transport.connectivity.FullConnectivityChecker;
import org.eclipse.jgit.internal.transport.parser.FirstCommand;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.BatchRefUpdate;
@@ -72,7 +73,6 @@ import org.eclipse.jgit.transport.ConnectivityChecker.ConnectivityCheckInfo;
import org.eclipse.jgit.transport.PacketLineIn.InputOverLimitIOException;
import org.eclipse.jgit.transport.ReceiveCommand.Result;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
-import org.eclipse.jgit.transport.internal.FullConnectivityChecker;
import org.eclipse.jgit.util.io.InterruptTimer;
import org.eclipse.jgit.util.io.LimitedInputStream;
import org.eclipse.jgit.util.io.TimeoutInputStream;
@@ -2061,6 +2061,16 @@ public class ReceivePack {
}
/**
+ * Get the current unpack error handler.
+ *
+ * @return the current unpack error handler.
+ * @since 5.8
+ */
+ public UnpackErrorHandler getUnpackErrorHandler() {
+ return unpackErrorHandler;
+ }
+
+ /**
* @param unpackErrorHandler
* the unpackErrorHandler to set
* @since 5.7
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigStore.java
new file mode 100644
index 0000000000..04a4922bb9
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigStore.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * An abstraction for a SSH config storage, like the OpenSSH ~/.ssh/config file.
+ *
+ * @since 5.8
+ */
+public interface SshConfigStore {
+
+ /**
+ * Locate the configuration for a specific host request.
+ *
+ * @param hostName
+ * to look up
+ * @param port
+ * the user supplied; <= 0 if none
+ * @param userName
+ * the user supplied, may be {@code null} or empty if none given
+ * @return the configuration for the requested name.
+ */
+ @NonNull
+ HostConfig lookup(@NonNull String hostName, int port, String userName);
+
+ /**
+ * A host entry from the ssh config. Any merging of global values and of
+ * several matching host entries, %-substitutions, and ~ replacement have
+ * all been done.
+ */
+ interface HostConfig {
+
+ /**
+ * Retrieves the value of a single-valued key, or the first if the key
+ * has multiple values. Keys are case-insensitive, so
+ * {@code getValue("HostName") == getValue("HOSTNAME")}.
+ *
+ * @param key
+ * to get the value of
+ * @return the value, or {@code null} if none
+ */
+ String getValue(String key);
+
+ /**
+ * Retrieves the values of a multi- or list-valued key. Keys are
+ * case-insensitive, so
+ * {@code getValue("HostName") == getValue("HOSTNAME")}.
+ *
+ * @param key
+ * to get the values of
+ * @return a possibly empty list of values
+ */
+ List<String> getValues(String key);
+
+ /**
+ * Retrieves an unmodifiable map of all single-valued options, with
+ * case-insensitive lookup by keys.
+ *
+ * @return all single-valued options
+ */
+ @NonNull
+ Map<String, String> getOptions();
+
+ /**
+ * Retrieves an unmodifiable map of all multi- or list-valued options,
+ * with case-insensitive lookup by keys.
+ *
+ * @return all multi-valued options
+ */
+ @NonNull
+ Map<String, List<String>> getMultiValuedOptions();
+
+ }
+
+ /**
+ * An empty {@link HostConfig}.
+ */
+ static final HostConfig EMPTY_CONFIG = new HostConfig() {
+
+ @Override
+ public String getValue(String key) {
+ return null;
+ }
+
+ @Override
+ public List<String> getValues(String key) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Map<String, String> getOptions() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<String, List<String>> getMultiValuedOptions() {
+ return Collections.emptyMap();
+ }
+
+ };
+}
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 e6d2042422..ef845f4dce 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
@@ -43,11 +43,12 @@ public abstract class SshSessionFactory {
}
return null;
}
+
/**
* Get the currently configured JVM-wide factory.
* <p>
- * A factory is always available. By default the factory will read from the
- * user's <code>$HOME/.ssh</code> and assume OpenSSH compatibility.
+ * By default the factory will read from the user's <code>$HOME/.ssh</code>
+ * and assume OpenSSH compatibility.
*
* @return factory the current factory for this JVM.
*/
@@ -60,7 +61,7 @@ public abstract class SshSessionFactory {
*
* @param newFactory
* factory for future sessions to be created through. If null the
- * default factory will be restored.s
+ * default factory will be restored.
*/
public static void setInstance(SshSessionFactory newFactory) {
if (newFactory != null) {
@@ -110,6 +111,15 @@ public abstract class SshSessionFactory {
throws TransportException;
/**
+ * The name of the type of session factory.
+ *
+ * @return the name of the type of session factory.
+ *
+ * @since 5.8
+ */
+ public abstract String getType();
+
+ /**
* Close (or recycle) a session to a host.
*
* @param session
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
index 947c4c3222..b9cb2484d8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
@@ -50,6 +50,8 @@ import org.eclipse.jgit.util.io.StreamCopyThread;
* enumeration, save file modification and hook execution.
*/
public class TransportGitSsh extends SshTransport implements PackTransport {
+ private static final String EXT = "ext"; //$NON-NLS-1$
+
static final TransportProtocol PROTO_SSH = new TransportProtocol() {
private final String[] schemeNames = { "ssh", "ssh+git", "git+ssh" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
@@ -127,6 +129,11 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
throws TransportException {
return new ExtSession();
}
+
+ @Override
+ public String getType() {
+ return EXT;
+ }
});
}
}
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 c3c1f2a405..16169f028b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -39,11 +39,13 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.HttpCookie;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.ProxySelector;
+import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
@@ -573,6 +575,14 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
}
} catch (NotSupportedException | TransportException e) {
throw e;
+ } catch (InterruptedIOException e) {
+ // Timeout!? Don't try other authentication methods.
+ throw new TransportException(uri, MessageFormat.format(
+ JGitText.get().connectionTimeOut, u.getHost()), e);
+ } catch (SocketException e) {
+ // Nothing on other end, timeout, connection reset, ...
+ throw new TransportException(uri,
+ JGitText.get().connectionFailed, e);
} catch (SSLHandshakeException e) {
handleSslFailure(e);
continue; // Re-try
@@ -1501,6 +1511,10 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
} catch (SSLHandshakeException e) {
handleSslFailure(e);
continue; // Re-try
+ } catch (SocketException | InterruptedIOException e) {
+ // Timeout!? Must propagate; don't try other authentication
+ // methods.
+ throw e;
} catch (IOException e) {
if (authenticator == null || authMethod
.getType() != HttpAuthMethod.Type.NONE) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
index c4d086d4bd..98c231a46d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
@@ -41,6 +41,12 @@ public interface HttpConnection {
int HTTP_OK = java.net.HttpURLConnection.HTTP_OK;
/**
+ * @see HttpURLConnection#HTTP_NOT_AUTHORITATIVE
+ * @since 5.8
+ */
+ int HTTP_NOT_AUTHORITATIVE = java.net.HttpURLConnection.HTTP_NOT_AUTHORITATIVE;
+
+ /**
* @see HttpURLConnection#HTTP_MOVED_PERM
* @since 4.7
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java
index 925c4e2f84..3b0bae21ef 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java
@@ -32,7 +32,7 @@ import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import org.eclipse.jgit.annotations.NonNull;
-import org.eclipse.jgit.transport.internal.DelegatingSSLSocketFactory;
+import org.eclipse.jgit.internal.transport.http.DelegatingSSLSocketFactory;
import org.eclipse.jgit.util.HttpSupport;
/**
* A {@link org.eclipse.jgit.transport.http.HttpConnection} which simply
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 988953b00c..91574efec4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -1036,12 +1036,36 @@ public abstract class FS {
public File userHome() {
Holder<File> p = userHome;
if (p == null) {
- p = new Holder<>(userHomeImpl());
+ p = new Holder<>(safeUserHomeImpl());
userHome = p;
}
return p.value;
}
+ private File safeUserHomeImpl() {
+ File home;
+ try {
+ home = userHomeImpl();
+ if (home != null) {
+ home.toPath();
+ return home;
+ }
+ } catch (RuntimeException e) {
+ LOG.error(JGitText.get().exceptionWhileFindingUserHome, e);
+ }
+ home = defaultUserHomeImpl();
+ if (home != null) {
+ try {
+ home.toPath();
+ return home;
+ } catch (InvalidPathException e) {
+ LOG.error(MessageFormat
+ .format(JGitText.get().invalidHomeDirectory, home), e);
+ }
+ }
+ return null;
+ }
+
/**
* Set the user's home directory location.
*
@@ -1081,6 +1105,10 @@ public abstract class FS {
* @return the user's home directory; null if the user does not have one.
*/
protected File userHomeImpl() {
+ return defaultUserHomeImpl();
+ }
+
+ private File defaultUserHomeImpl() {
final String home = AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty("user.home") //$NON-NLS-1$
);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
index deab4e67a0..c33c869b64 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
@@ -140,19 +140,19 @@ public final class EolStreamTypeUtil {
}
// new git system
+ if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
+ return EolStreamType.AUTO_LF;
+ }
+
String eol = attrs.getValue("eol"); //$NON-NLS-1$
- if (eol != null)
+ if (eol != null) {
// check-in is always normalized to LF
return EolStreamType.TEXT_LF;
-
+ }
if (attrs.isSet("text")) { //$NON-NLS-1$
return EolStreamType.TEXT_LF;
}
- if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
- return EolStreamType.AUTO_LF;
- }
-
switch (options.getAutoCRLF()) {
case TRUE:
case INPUT:
@@ -168,6 +168,8 @@ public final class EolStreamTypeUtil {
switch (options.getAutoCRLF()) {
case TRUE:
return EolStreamType.TEXT_CRLF;
+ case INPUT:
+ return EolStreamType.DIRECT;
default:
// no decision
}
@@ -205,7 +207,10 @@ public final class EolStreamTypeUtil {
// new git system
String eol = attrs.getValue("eol"); //$NON-NLS-1$
if (eol != null) {
- if ("crlf".equals(eol)) {//$NON-NLS-1$
+ if ("crlf".equals(eol)) { //$NON-NLS-1$
+ if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
+ return EolStreamType.AUTO_CRLF;
+ }
return EolStreamType.TEXT_CRLF;
} else if ("lf".equals(eol)) { //$NON-NLS-1$
return EolStreamType.DIRECT;