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_filters24
-rw-r--r--org.eclipse.jgit/BUILD3
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF111
-rw-r--r--org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit/pom.xml18
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java58
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java42
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java94
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java61
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java23
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java88
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java81
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java42
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/errors/JGitInternalException.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnsupportedSigningFormatException.java69
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java68
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/SequenceComparator.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java48
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BlockBasedFile.java47
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java161
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java24
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java426
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftable.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java69
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaEncoder.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java19
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/RefCursor.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableCompactor.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Scanner.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstCommand.java108
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstWant.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java105
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignature.java98
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java138
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java91
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java23
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java59
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java26
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java37
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKey.java71
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java373
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyPassphrasePrompt.java134
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java160
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java92
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java16
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalHttpServerGlue.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java106
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java143
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java65
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/Sha1CollisionException.java2
93 files changed, 2987 insertions, 735 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index cfc68662f0..bb37dd8c8a 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -3,8 +3,8 @@
<resource path="META-INF/MANIFEST.MF">
<filter id="924844039">
<message_arguments>
- <message_argument value="5.2.3"/>
- <message_argument value="5.2.0"/>
+ <message_argument value="5.3.3"/>
+ <message_argument value="5.3.0"/>
</message_arguments>
</filter>
</resource>
@@ -118,20 +118,6 @@
</message_arguments>
</filter>
</resource>
- <resource path="src/org/eclipse/jgit/transport/TransferConfig.java" type="org.eclipse.jgit.transport.TransferConfig">
- <filter id="1159725059">
- <message_arguments>
- <message_argument value="5.1.4"/>
- <message_argument value="TransferConfig(Config)"/>
- </message_arguments>
- </filter>
- <filter id="1159725059">
- <message_arguments>
- <message_argument value="5.1.4"/>
- <message_argument value="TransferConfig(Repository)"/>
- </message_arguments>
- </filter>
- </resource>
<resource path="src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java" type="org.eclipse.jgit.treewalk.WorkingTreeIterator">
<filter id="1142947843">
<message_arguments>
@@ -157,12 +143,6 @@
<resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS">
<filter id="1142947843">
<message_arguments>
- <message_argument value="4.5.6"/>
- <message_argument value="fileAttributes(File)"/>
- </message_arguments>
- </filter>
- <filter id="1142947843">
- <message_arguments>
<message_argument value="5.1.9"/>
<message_argument value="getFileStoreAttributes(Path)"/>
</message_arguments>
diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD
index 6ba7796b7e..b67bfac5b6 100644
--- a/org.eclipse.jgit/BUILD
+++ b/org.eclipse.jgit/BUILD
@@ -22,6 +22,9 @@ java_library(
resources = RESOURCES,
deps = [
":insecure_cipher_factory",
+ "//lib:bcpg",
+ "//lib:bcpkix",
+ "//lib:bcprov",
"//lib:javaewah",
"//lib:jsch",
"//lib:jzlib",
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 66701b0b38..d4226925bd 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -3,12 +3,12 @@ Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Automatic-Module-Name: org.eclipse.jgit
Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 5.2.3.qualifier
+Bundle-Version: 5.3.3.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.annotations;version="5.2.3",
- org.eclipse.jgit.api;version="5.2.3";
+Export-Package: org.eclipse.jgit.annotations;version="5.3.3",
+ org.eclipse.jgit.api;version="5.3.3";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff,
@@ -22,53 +22,53 @@ Export-Package: org.eclipse.jgit.annotations;version="5.2.3",
org.eclipse.jgit.submodule,
org.eclipse.jgit.transport,
org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="5.2.3",
- org.eclipse.jgit.blame;version="5.2.3";
+ org.eclipse.jgit.api.errors;version="5.3.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="5.3.3",
+ org.eclipse.jgit.blame;version="5.3.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="5.2.3";
+ org.eclipse.jgit.diff;version="5.3.3";
uses:="org.eclipse.jgit.patch,
org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="5.2.3";
+ org.eclipse.jgit.dircache;version="5.3.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util,
org.eclipse.jgit.events,
org.eclipse.jgit.attributes",
- org.eclipse.jgit.errors;version="5.2.3";
+ org.eclipse.jgit.errors;version="5.3.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.internal.storage.pack,
org.eclipse.jgit.transport,
org.eclipse.jgit.dircache",
- org.eclipse.jgit.events;version="5.2.3";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="5.2.3",
- org.eclipse.jgit.gitrepo;version="5.2.3";
+ org.eclipse.jgit.events;version="5.3.3";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.fnmatch;version="5.3.3",
+ org.eclipse.jgit.gitrepo;version="5.3.3";
uses:="org.eclipse.jgit.api,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.xml.sax.helpers,
org.xml.sax",
- org.eclipse.jgit.gitrepo.internal;version="5.2.3";x-internal:=true,
- org.eclipse.jgit.hooks;version="5.2.3";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="5.2.3",
- org.eclipse.jgit.ignore.internal;version="5.2.3";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="5.2.3";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.fsck;version="5.2.3";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.ketch;version="5.2.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.revwalk;version="5.2.3";x-internal:=true,
- org.eclipse.jgit.internal.storage.dfs;version="5.2.3";
+ org.eclipse.jgit.gitrepo.internal;version="5.3.3";x-internal:=true,
+ org.eclipse.jgit.hooks;version="5.3.3";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="5.3.3",
+ org.eclipse.jgit.ignore.internal;version="5.3.3";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="5.3.3";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.fsck;version="5.3.3";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.ketch;version="5.3.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.revwalk;version="5.3.3";x-internal:=true,
+ org.eclipse.jgit.internal.storage.dfs;version="5.3.3";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.http.server,
org.eclipse.jgit.http.test,
org.eclipse.jgit.lfs.test",
- org.eclipse.jgit.internal.storage.file;version="5.2.3";
+ org.eclipse.jgit.internal.storage.file;version="5.3.3";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.junit,
org.eclipse.jgit.junit.http,
@@ -77,18 +77,18 @@ Export-Package: org.eclipse.jgit.annotations;version="5.2.3",
org.eclipse.jgit.pgm,
org.eclipse.jgit.pgm.test,
org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.internal.storage.io;version="5.2.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.pack;version="5.2.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftable;version="5.2.3";
+ org.eclipse.jgit.internal.storage.io;version="5.3.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.pack;version="5.3.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftable;version="5.3.3";
x-friends:="org.eclipse.jgit.http.test,
org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftree;version="5.2.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.submodule;version="5.2.3";x-internal:=true,
- org.eclipse.jgit.internal.transport.parser;version="5.2.3";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.server",
- org.eclipse.jgit.internal.transport.ssh;version="5.2.3";x-friends:="org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.lib;version="5.2.3";
+ org.eclipse.jgit.internal.storage.reftree;version="5.3.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.submodule;version="5.3.3";x-internal:=true,
+ org.eclipse.jgit.internal.transport.parser;version="5.3.3";x-friends:="org.eclipse.jgit.http.server,org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.transport.ssh;version="5.3.3";x-friends:="org.eclipse.jgit.ssh.apache",
+ org.eclipse.jgit.lib;version="5.3.3";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util,
@@ -98,33 +98,33 @@ Export-Package: org.eclipse.jgit.annotations;version="5.2.3",
org.eclipse.jgit.treewalk,
org.eclipse.jgit.transport,
org.eclipse.jgit.submodule",
- org.eclipse.jgit.lib.internal;version="5.2.3";x-internal:=true,
- org.eclipse.jgit.merge;version="5.2.3";
+ org.eclipse.jgit.lib.internal;version="5.3.3";x-internal:=true,
+ org.eclipse.jgit.merge;version="5.3.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.diff,
org.eclipse.jgit.dircache,
org.eclipse.jgit.api",
- org.eclipse.jgit.nls;version="5.2.3",
- org.eclipse.jgit.notes;version="5.2.3";
+ org.eclipse.jgit.nls;version="5.3.3",
+ org.eclipse.jgit.notes;version="5.3.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="5.2.3";
+ org.eclipse.jgit.patch;version="5.3.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="5.3.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.revwalk;version="5.3.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff,
org.eclipse.jgit.revwalk.filter",
- org.eclipse.jgit.revwalk.filter;version="5.2.3";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="5.2.3";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
- org.eclipse.jgit.transport;version="5.2.3";
+ org.eclipse.jgit.revwalk.filter;version="5.3.3";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="5.3.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="5.3.3";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="5.3.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.transport;version="5.3.3";
uses:="org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.internal.storage.pack,
@@ -132,33 +132,44 @@ Export-Package: org.eclipse.jgit.annotations;version="5.2.3",
org.eclipse.jgit.util,
org.eclipse.jgit.util.io,
org.eclipse.jgit.internal.storage.file,
+ org.eclipse.jgit.internal.transport.parser,
org.eclipse.jgit.lib,
org.eclipse.jgit.transport.http,
org.eclipse.jgit.errors,
org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="5.2.3";uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
- org.eclipse.jgit.treewalk;version="5.2.3";
+ org.eclipse.jgit.transport.http;version="5.3.3";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="5.3.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.treewalk;version="5.3.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.attributes,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util,
org.eclipse.jgit.dircache",
- org.eclipse.jgit.treewalk.filter;version="5.2.3";uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="5.2.3";
+ org.eclipse.jgit.treewalk.filter;version="5.3.3";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util;version="5.3.3";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.transport.http,
org.eclipse.jgit.storage.file,
org.ietf.jgss",
- org.eclipse.jgit.util.io;version="5.2.3",
- org.eclipse.jgit.util.sha1;version="5.2.3",
- org.eclipse.jgit.util.time;version="5.2.3"
+ org.eclipse.jgit.util.io;version="5.3.3",
+ org.eclipse.jgit.util.sha1;version="5.3.3",
+ org.eclipse.jgit.util.time;version="5.3.3"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
com.jcraft.jsch;version="[0.1.37,0.2.0)",
javax.crypto,
javax.net.ssl,
+ org.bouncycastle;version="[1.60.0,2.0.0)",
+ org.bouncycastle.bcpg;version="[1.60.0,2.0.0)",
+ org.bouncycastle.gpg;version="[1.60.0,2.0.0)",
+ org.bouncycastle.gpg.keybox;version="[1.60.0,2.0.0)",
+ org.bouncycastle.jce.provider;version="[1.60.0,2.0.0)",
+ org.bouncycastle.openpgp;version="[1.60.0,2.0.0)",
+ org.bouncycastle.openpgp.jcajce;version="[1.60.0,2.0.0)",
+ org.bouncycastle.openpgp.operator;version="[1.60.0,2.0.0)",
+ org.bouncycastle.openpgp.operator.jcajce;version="[1.60.0,2.0.0)",
+ org.bouncycastle.util.encoders;version="[1.60.0,2.0.0)",
org.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 8e22086cce..e3fd47d5c7 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit - Sources
Bundle-SymbolicName: org.eclipse.jgit.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.2.3.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="5.2.3.qualifier";roots="."
+Bundle-Version: 5.3.3.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="5.3.3.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index c30d648c69..6097858185 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit/pom.xml
@@ -53,7 +53,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.2.3-SNAPSHOT</version>
+ <version>5.3.3-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit</artifactId>
@@ -84,12 +84,26 @@
<artifactId>JavaEWAH</artifactId>
</dependency>
-
<dependency>
<groupId>org.slf4j</groupId>
<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>
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 9ab03c251c..91136620c4 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -225,6 +225,7 @@ createBranchUnexpectedResult=Create branch returned unexpected result {0}
createNewFileFailed=Could not create new file {0}
createRequiresZeroOldId=Create requires old ID to be zero
credentialPassword=Password
+credentialPassphrase=Passphrase
credentialUsername=Username
daemonAlreadyRunning=Daemon already running
daysAgo={0} days ago
@@ -326,6 +327,14 @@ gcFailed=Garbage collection failed.
gcLogExists=A previous GC run reported an error: ''{0}''. Automatic gc will fail until ''{1}'' is removed.
gcTooManyUnpruned=Too many loose, unpruneable objects after garbage collection. Consider adjusting gc.auto or gc.pruneExpire.
gitmodulesNotFound=.gitmodules not found in tree.
+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}
+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
@@ -510,6 +519,7 @@ oldIdMustNotBeNull=Expected old ID must not be null
onlyAlreadyUpToDateAndFastForwardMergesAreAvailable=only already-up-to-date and fast forward merges are available
onlyOneFetchSupported=Only one fetch supported
onlyOneOperationCallPerConnectionIsSupported=Only one operation call per connection is supported.
+onlyOpenPgpSupportedForSigning=OpenPGP is the only supported signing option with JGit at this time (gpg.format must be set to openpgp).
openFilesMustBeAtLeast1=Open files must be >= 1
openingConnection=Opening connection
operationCanceled=Operation {0} was canceled
@@ -588,8 +598,9 @@ remoteConfigHasNoURIAssociated=Remote config "{0}" has no URIs associated
remoteDoesNotHaveSpec=Remote does not have {0} available for fetch.
remoteDoesNotSupportSmartHTTPPush=remote does not support smart HTTP push
remoteHungUpUnexpectedly=remote hung up unexpectedly
-remoteNameCantBeNull=Remote name can't be null.
-renameBranchFailedBecauseTag=Can not rename as Ref {0} is a tag
+remoteNameCannotBeNull=Remote name cannot be null.
+renameBranchFailedAmbiguous=Cannot rename branch {0}; name is ambiguous: {1} or {2}
+renameBranchFailedNotABranch=Cannot rename {0}: this is not a branch
renameBranchFailedUnknownReason=Rename failed with unknown reason
renameBranchUnexpectedResult=Unexpected rename result {0}
renameCancelled=Rename detection was cancelled
@@ -629,7 +640,7 @@ selectingCommits=Selecting commits
sequenceTooLargeForDiffAlgorithm=Sequence too large for difference algorithm.
serviceNotEnabledNoName=Service not enabled
serviceNotPermitted={1} not permitted on ''{0}''
-sha1CollisionDetected1=SHA-1 collision detected on {0}
+sha1CollisionDetected=SHA-1 collision detected on {0}
shallowCommitsAlreadyInitialized=Shallow commits have already been initialized
shallowPacksRequireDepthWalk=Shallow packs require a DepthWalk
shortCompressedStreamAt=Short compressed stream at {0}
@@ -726,6 +737,7 @@ unableToReadPackfile=Unable to read packfile {0}
unableToRemovePath=Unable to remove path ''{0}''
unableToStore=Unable to store {0}.
unableToWrite=Unable to write {0}
+unableToSignCommitNoSecretKey=Unable to sign commit. Signing key not available.
unauthorized=Unauthorized
underflowedReftableBlock=Underflowed reftable block
unencodeableFile=Unencodable file: {0}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
index 455a2e665f..e05f6f1bd6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -162,7 +162,9 @@ public class CheckoutCommand extends GitCommand<Ref> {
private String name;
- private boolean force = false;
+ private boolean forceRefUpdate = false;
+
+ private boolean forced = false;
private boolean createBranch = false;
@@ -269,7 +271,11 @@ public class CheckoutCommand extends GitCommand<Ref> {
try {
dco = new DirCacheCheckout(repo, headTree, dc,
newCommit.getTree());
- dco.setFailOnConflict(!force);
+ dco.setFailOnConflict(true);
+ dco.setForce(forced);
+ if (forced) {
+ dco.setFailOnConflict(false);
+ }
dco.setProgressMonitor(monitor);
try {
dco.checkout();
@@ -286,7 +292,7 @@ public class CheckoutCommand extends GitCommand<Ref> {
ref = null;
String toName = Repository.shortenRefName(name);
RefUpdate refUpdate = repo.updateRef(Constants.HEAD, ref == null);
- refUpdate.setForceUpdate(force);
+ refUpdate.setForceUpdate(forceRefUpdate);
refUpdate.setRefLogMessage(refLogMessage + " to " + toName, false); //$NON-NLS-1$
Result updateResult;
if (ref != null)
@@ -666,10 +672,54 @@ public class CheckoutCommand extends GitCommand<Ref> {
* set to a new start-point; if false, the existing branch will
* not be changed
* @return this instance
+ * @deprecated this method was badly named comparing its semantics to native
+ * git's checkout --force option, use
+ * {@link #setForceRefUpdate(boolean)} instead
*/
+ @Deprecated
public CheckoutCommand setForce(boolean force) {
+ return setForceRefUpdate(force);
+ }
+
+ /**
+ * Specify to force the ref update in case of a branch switch.
+ *
+ * In releases prior to 5.2 this method was called setForce() but this name
+ * was misunderstood to implement native git's --force option, which is not
+ * true.
+ *
+ * @param forceRefUpdate
+ * if <code>true</code> and the branch with the given name
+ * already exists, the start-point of an existing branch will be
+ * set to a new start-point; if false, the existing branch will
+ * not be changed
+ * @return this instance
+ * @since 5.3
+ */
+ public CheckoutCommand setForceRefUpdate(boolean forceRefUpdate) {
+ checkCallable();
+ this.forceRefUpdate = forceRefUpdate;
+ return this;
+ }
+
+ /**
+ * Allow a checkout even if the workingtree or index differs from HEAD. This
+ * matches native git's '--force' option.
+ *
+ * JGit releases before 5.2 had a method <code>setForce()</code> offering
+ * semantics different from this new <code>setForced()</code>. This old
+ * semantic can now be found in {@link #setForceRefUpdate(boolean)}
+ *
+ * @param forced
+ * if set to <code>true</code> then allow the checkout even if
+ * workingtree or index doesn't match HEAD. Overwrite workingtree
+ * files and index content with the new content in this case.
+ * @return this instance
+ * @since 5.3
+ */
+ public CheckoutCommand setForced(boolean forced) {
checkCallable();
- this.force = force;
+ this.forced = forced;
return this;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
index 73af8ba16d..0248ba2793 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -303,18 +303,25 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
}
private List<RefSpec> calculateRefSpecs(boolean fetchAll, String dst) {
- RefSpec wcrs = new RefSpec();
- wcrs = wcrs.setForceUpdate(true);
- wcrs = wcrs.setSourceDestination(Constants.R_HEADS + '*', dst);
+ RefSpec heads = new RefSpec();
+ heads = heads.setForceUpdate(true);
+ heads = heads.setSourceDestination(Constants.R_HEADS + '*', dst);
List<RefSpec> specs = new ArrayList<>();
if (!fetchAll) {
+ RefSpec tags = new RefSpec();
+ tags = tags.setForceUpdate(true);
+ tags = tags.setSourceDestination(Constants.R_TAGS + '*',
+ Constants.R_TAGS + '*');
for (String selectedRef : branchesToClone) {
- if (wcrs.matchSource(selectedRef)) {
- specs.add(wcrs.expandFromSource(selectedRef));
+ if (heads.matchSource(selectedRef)) {
+ specs.add(heads.expandFromSource(selectedRef));
+ } else if (tags.matchSource(selectedRef)) {
+ specs.add(tags.expandFromSource(selectedRef));
}
}
} else {
- specs.add(wcrs);
+ // We'll fetch the tags anyway.
+ specs.add(heads);
}
return specs;
}
@@ -590,11 +597,15 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
}
/**
- * Set whether all branches have to be fetched
+ * Set whether all branches have to be fetched.
+ * <p>
+ * If {@code false}, use {@link #setBranchesToClone(Collection)} to define
+ * what will be cloned. If neither are set, all branches will be cloned.
+ * </p>
*
* @param cloneAllBranches
- * true when all branches have to be fetched (indicates wildcard
- * in created fetch refspec), false otherwise.
+ * {@code true} when all branches have to be fetched (indicates
+ * wildcard in created fetch refspec), {@code false} otherwise.
* @return {@code this}
*/
public CloneCommand setCloneAllBranches(boolean cloneAllBranches) {
@@ -616,12 +627,17 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
}
/**
- * Set branches to clone
+ * Set the branches or tags to clone.
+ * <p>
+ * This is ignored if {@link #setCloneAllBranches(boolean)
+ * setCloneAllBranches(true)} is used. If {@code branchesToClone} is
+ * {@code null} or empty, it's also ignored and all branches will be cloned.
+ * </p>
*
* @param branchesToClone
- * collection of branches to clone. Ignored when allSelected is
- * true. Must be specified as full ref names (e.g.
- * <code>refs/heads/master</code>).
+ * collection of branches to clone. Must be specified as full ref
+ * names (e.g. {@code refs/heads/master} or
+ * {@code refs/tags/v1.0.0}).
* @return {@code this}
*/
public CloneCommand setBranchesToClone(Collection<String> branchesToClone) {
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 cea28fac18..61d51cc913 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -61,6 +61,7 @@ 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.UnmergedPathsException;
+import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuildIterator;
@@ -76,6 +77,9 @@ import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.GpgSigner;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
@@ -84,10 +88,12 @@ 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;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
@@ -139,6 +145,14 @@ public class CommitCommand extends GitCommand<RevCommit> {
private Boolean allowEmpty;
+ private Boolean signCommit;
+
+ private String signingKey;
+
+ private GpgSigner gpgSigner;
+
+ private CredentialsProvider credentialsProvider;
+
/**
* Constructor for CommitCommand
*
@@ -147,6 +161,7 @@ public class CommitCommand extends GitCommand<RevCommit> {
*/
protected CommitCommand(Repository repo) {
super(repo);
+ this.credentialsProvider = CredentialsProvider.getDefault();
}
/**
@@ -251,6 +266,12 @@ public class CommitCommand extends GitCommand<RevCommit> {
commit.setParentIds(parents);
commit.setTreeId(indexTreeId);
+
+ if (signCommit.booleanValue()) {
+ gpgSigner.sign(commit, signingKey, committer,
+ credentialsProvider);
+ }
+
ObjectId commitId = odi.insert(commit);
odi.flush();
@@ -517,9 +538,10 @@ 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
*/
private void processOptions(RepositoryState state, RevWalk rw)
- throws NoMessageException {
+ throws NoMessageException, UnsupportedSigningFormatException {
if (committer == null)
committer = new PersonIdent(repo);
if (author == null && !amend)
@@ -572,6 +594,25 @@ public class CommitCommand extends GitCommand<RevCommit> {
// as long as we don't support -C option we have to have
// an explicit message
throw new NoMessageException(JGitText.get().commitMessageNotSpecified);
+
+ GpgConfig gpgConfig = new GpgConfig(repo.getConfig());
+ if (signCommit == null) {
+ signCommit = gpgConfig.isSignCommits() ? Boolean.TRUE
+ : Boolean.FALSE;
+ }
+ if (signingKey == null) {
+ signingKey = gpgConfig.getSigningKey();
+ }
+ if (gpgSigner == null) {
+ if (gpgConfig.getKeyFormat() != GpgFormat.OPENPGP) {
+ throw new UnsupportedSigningFormatException(
+ JGitText.get().onlyOpenPgpSupportedForSigning);
+ }
+ gpgSigner = GpgSigner.getDefault();
+ if (gpgSigner == null) {
+ gpgSigner = new BouncyCastleGpgSigner();
+ }
+ }
}
private boolean isMergeDuringRebase(RepositoryState state) {
@@ -873,4 +914,55 @@ public class CommitCommand extends GitCommand<RevCommit> {
hookOutRedirect.put(hookName, hookStdOut);
return this;
}
+
+ /**
+ * Sets the signing key
+ * <p>
+ * Per spec of user.signingKey: this will be sent to the GPG program as is,
+ * i.e. can be anything supported by the GPG program.
+ * </p>
+ * <p>
+ * Note, if none was set or <code>null</code> is specified a default will be
+ * obtained from the configuration.
+ * </p>
+ *
+ * @param signingKey
+ * signing key (maybe <code>null</code>)
+ * @return {@code this}
+ * @since 5.3
+ */
+ public CommitCommand setSigningKey(String signingKey) {
+ checkCallable();
+ this.signingKey = signingKey;
+ return this;
+ }
+
+ /**
+ * Sets whether the commit should be signed.
+ *
+ * @param sign
+ * <code>true</code> to sign, <code>false</code> to not sign and
+ * <code>null</code> for default behavior (read from
+ * configuration)
+ * @return {@code this}
+ * @since 5.3
+ */
+ public CommitCommand setSign(Boolean sign) {
+ checkCallable();
+ this.signCommit = sign;
+ return this;
+ }
+
+ /**
+ * Sets a {@link CredentialsProvider}
+ *
+ * @param credentialsProvider
+ * the provider to use when querying for credentials (eg., during
+ * signing)
+ * @since 5.3
+ */
+ public void setCredentialsProvider(
+ CredentialsProvider credentialsProvider) {
+ this.credentialsProvider = credentialsProvider;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
index 73e93a1c94..2c9c5f20cc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -193,7 +193,8 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
// updated to an object that is not currently present in the
// submodule.
if ((recurseMode == FetchRecurseSubmodulesMode.ON_DEMAND
- && !submoduleRepo.hasObject(walk.getObjectId()))
+ && !submoduleRepo.getObjectDatabase()
+ .has(walk.getObjectId()))
|| recurseMode == FetchRecurseSubmodulesMode.YES) {
FetchCommand f = new FetchCommand(submoduleRepo)
.setProgressMonitor(monitor)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index 9653c365b2..0e3d000d3a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -158,11 +158,14 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private static final String ONTO = "onto"; //$NON-NLS-1$
- private static final String ONTO_NAME = "onto-name"; //$NON-NLS-1$
+ private static final String ONTO_NAME = "onto_name"; //$NON-NLS-1$
private static final String PATCH = "patch"; //$NON-NLS-1$
- private static final String REBASE_HEAD = "head"; //$NON-NLS-1$
+ private static final String REBASE_HEAD = "orig-head"; //$NON-NLS-1$
+
+ /** Pre git 1.7.6 file name for {@link #REBASE_HEAD}. */
+ private static final String REBASE_HEAD_LEGACY = "head"; //$NON-NLS-1$
private static final String AMEND = "amend"; //$NON-NLS-1$
@@ -177,6 +180,10 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
/**
* The folder containing the hashes of (potentially) rewritten commits when
* --preserve-merges is used.
+ * <p>
+ * Native git rebase --merge uses a <em>file</em> of that name to record
+ * commits to copy notes at the end of the whole rebase.
+ * </p>
*/
private static final String REWRITTEN = "rewritten"; //$NON-NLS-1$
@@ -289,7 +296,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
this.upstreamCommit = walk.parseCommit(repo
.resolve(upstreamCommitId));
- preserveMerges = rebaseState.getRewrittenDir().exists();
+ preserveMerges = rebaseState.getRewrittenDir().isDirectory();
break;
case BEGIN:
autoStash();
@@ -1120,10 +1127,14 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
repo.writeOrigHead(headId);
rebaseState.createFile(REBASE_HEAD, headId.name());
+ rebaseState.createFile(REBASE_HEAD_LEGACY, headId.name());
rebaseState.createFile(HEAD_NAME, headName);
rebaseState.createFile(ONTO, upstreamCommit.name());
rebaseState.createFile(ONTO_NAME, upstreamCommitName);
- if (isInteractive()) {
+ if (isInteractive() || preserveMerges) {
+ // --preserve-merges is an interactive mode for native git. Without
+ // this, native git rebase --continue after a conflict would fall
+ // into merge mode.
rebaseState.createFile(INTERACTIVE, ""); //$NON-NLS-1$
}
rebaseState.createFile(QUIET, ""); //$NON-NLS-1$
@@ -1333,8 +1344,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private RebaseResult abort(RebaseResult result) throws IOException,
GitAPIException {
+ ObjectId origHead = getOriginalHead();
try {
- ObjectId origHead = repo.readOrigHead();
String commitId = origHead != null ? origHead.name() : null;
monitor.beginTask(MessageFormat.format(
JGitText.get().abortingRebase, commitId),
@@ -1373,7 +1384,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
// update the HEAD
res = refUpdate.link(headName);
} else {
- refUpdate.setNewObjectId(repo.readOrigHead());
+ refUpdate.setNewObjectId(origHead);
res = refUpdate.forceUpdate();
}
@@ -1400,6 +1411,19 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
}
+ private ObjectId getOriginalHead() throws IOException {
+ try {
+ return ObjectId.fromString(rebaseState.readFile(REBASE_HEAD));
+ } catch (FileNotFoundException e) {
+ try {
+ return ObjectId
+ .fromString(rebaseState.readFile(REBASE_HEAD_LEGACY));
+ } catch (FileNotFoundException ex) {
+ return repo.readOrigHead();
+ }
+ }
+ }
+
private boolean checkoutCommit(String headName, RevCommit commit)
throws IOException,
CheckoutConflictException {
@@ -1706,7 +1730,20 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
public String readFile(String name) throws IOException {
- return readFile(getDir(), name);
+ try {
+ return readFile(getDir(), name);
+ } catch (FileNotFoundException e) {
+ if (ONTO_NAME.equals(name)) {
+ // Older JGit mistakenly wrote a file "onto-name" instead of
+ // "onto_name". Try that wrong name just in case somebody
+ // upgraded while a rebase started by JGit was in progress.
+ File oldFile = getFile(ONTO_NAME.replace('_', '-'));
+ if (oldFile.exists()) {
+ return readFile(oldFile);
+ }
+ }
+ throw e;
+ }
}
public void createFile(String name, String content) throws IOException {
@@ -1721,14 +1758,18 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
return (getDir().getName() + "/" + name); //$NON-NLS-1$
}
- private static String readFile(File directory, String fileName)
- throws IOException {
- byte[] content = IO.readFully(new File(directory, fileName));
+ private static String readFile(File file) throws IOException {
+ byte[] content = IO.readFully(file);
// strip off the last LF
int end = RawParseUtils.prevLF(content, content.length);
return RawParseUtils.decode(content, 0, end + 1);
}
+ private static String readFile(File directory, String fileName)
+ throws IOException {
+ return readFile(new File(directory, fileName));
+ }
+
private static void createFile(File parentDir, String name,
String content)
throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
index 7a5885cfda..016cb15d90 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
@@ -65,7 +65,7 @@ import org.eclipse.jgit.transport.RemoteConfig;
*/
public class RemoteRemoveCommand extends GitCommand<RemoteConfig> {
- private String name;
+ private String remoteName;
/**
* <p>
@@ -84,9 +84,24 @@ public class RemoteRemoveCommand extends GitCommand<RemoteConfig> {
*
* @param name
* a remote name
+ * @deprecated use {@link #setRemoteName} instead
*/
+ @Deprecated
public void setName(String name) {
- this.name = name;
+ this.remoteName = name;
+ }
+
+ /**
+ * The name of the remote to remove.
+ *
+ * @param remoteName
+ * a remote name
+ * @return {@code this}
+ * @since 5.3
+ */
+ public RemoteRemoveCommand setRemoteName(String remoteName) {
+ this.remoteName = remoteName;
+ return this;
}
/**
@@ -101,8 +116,8 @@ public class RemoteRemoveCommand extends GitCommand<RemoteConfig> {
try {
StoredConfig config = repo.getConfig();
- RemoteConfig remote = new RemoteConfig(config, name);
- config.unsetSection(ConfigConstants.CONFIG_KEY_REMOTE, name);
+ RemoteConfig remote = new RemoteConfig(config, remoteName);
+ config.unsetSection(ConfigConstants.CONFIG_KEY_REMOTE, remoteName);
config.save();
return remote;
} catch (IOException | URISyntaxException e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
index d7b7a31bd6..21d4023d67 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
@@ -54,7 +54,7 @@ import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
/**
- * Used to to change the URL of a remote.
+ * Used to change the URL of a remote.
*
* This class has setters for all supported options and arguments of this
* command and a {@link #call()} method to finally execute the command.
@@ -66,11 +66,28 @@ import org.eclipse.jgit.transport.URIish;
*/
public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
- private String name;
+ /**
+ * The available URI types for the remote.
+ *
+ * @since 5.3
+ */
+ public enum UriType {
+ /**
+ * Fetch URL for the remote.
+ */
+ FETCH,
+ /**
+ * Push URL for the remote.
+ */
+ PUSH
+ }
- private URIish uri;
- private boolean push;
+ private String remoteName;
+
+ private URIish remoteUri;
+
+ private UriType type;
/**
* <p>
@@ -89,9 +106,24 @@ public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
*
* @param name
* a remote name
+ * @deprecated use {@link #setRemoteName} instead
*/
+ @Deprecated
public void setName(String name) {
- this.name = name;
+ this.remoteName = name;
+ }
+
+ /**
+ * The name of the remote to change the URL for.
+ *
+ * @param remoteName
+ * a remote remoteName
+ * @return {@code this}
+ * @since 5.3
+ */
+ public RemoteSetUrlCommand setRemoteName(String remoteName) {
+ this.remoteName = remoteName;
+ return this;
}
/**
@@ -99,9 +131,24 @@ public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
*
* @param uri
* an URL for the remote
+ * @deprecated use {@link #setRemoteUri} instead
*/
+ @Deprecated
public void setUri(URIish uri) {
- this.uri = uri;
+ this.remoteUri = uri;
+ }
+
+ /**
+ * The new URL for the remote.
+ *
+ * @param remoteUri
+ * an URL for the remote
+ * @return {@code this}
+ * @since 5.3
+ */
+ public RemoteSetUrlCommand setRemoteUri(URIish remoteUri) {
+ this.remoteUri = remoteUri;
+ return this;
}
/**
@@ -110,9 +157,28 @@ public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
* @param push
* <code>true</code> to set the push url, <code>false</code> to
* set the fetch url
+ * @deprecated use {@link #setUriType} instead
*/
+ @Deprecated
public void setPush(boolean push) {
- this.push = push;
+ if (push) {
+ setUriType(UriType.PUSH);
+ } else {
+ setUriType(UriType.FETCH);
+ }
+ }
+
+ /**
+ * Whether to change the push URL of the remote instead of the fetch URL.
+ *
+ * @param type
+ * the <code>UriType</code> value to set
+ * @return {@code this}
+ * @since 5.3
+ */
+ public RemoteSetUrlCommand setUriType(UriType type) {
+ this.type = type;
+ return this;
}
/**
@@ -127,8 +193,8 @@ public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
try {
StoredConfig config = repo.getConfig();
- RemoteConfig remote = new RemoteConfig(config, name);
- if (push) {
+ RemoteConfig remote = new RemoteConfig(config, remoteName);
+ if (type == UriType.PUSH) {
List<URIish> uris = remote.getPushURIs();
if (uris.size() > 1) {
throw new JGitInternalException(
@@ -136,7 +202,7 @@ public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
} else if (uris.size() == 1) {
remote.removePushURI(uris.get(0));
}
- remote.addPushURI(uri);
+ remote.addPushURI(remoteUri);
} else {
List<URIish> uris = remote.getURIs();
if (uris.size() > 1) {
@@ -145,7 +211,7 @@ public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
} else if (uris.size() == 1) {
remote.removeURI(uris.get(0));
}
- remote.addURI(uri);
+ remote.addURI(remoteUri);
}
remote.update(config);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
index 24d9dd4015..7e8c33c8a1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
@@ -94,25 +94,38 @@ public class RenameBranchCommand extends GitCommand<Ref> {
RefAlreadyExistsException, DetachedHeadException {
checkCallable();
- if (newName == null)
+ if (newName == null) {
throw new InvalidRefNameException(MessageFormat.format(JGitText
.get().branchNameInvalid, "<null>")); //$NON-NLS-1$
-
+ }
try {
String fullOldName;
String fullNewName;
- if (repo.findRef(newName) != null)
- throw new RefAlreadyExistsException(MessageFormat.format(
- JGitText.get().refAlreadyExists1, newName));
if (oldName != null) {
- Ref ref = repo.findRef(oldName);
- if (ref == null)
- throw new RefNotFoundException(MessageFormat.format(
- JGitText.get().refNotResolved, oldName));
- if (ref.getName().startsWith(Constants.R_TAGS))
- throw new RefNotFoundException(MessageFormat.format(
- JGitText.get().renameBranchFailedBecauseTag,
- oldName));
+ // Don't just rely on findRef -- if there are local and remote
+ // branches with the same name, and oldName is a short name, it
+ // does not uniquely identify the ref and we might end up
+ // renaming the wrong branch or finding a tag instead even
+ // if a unique branch for the name exists!
+ //
+ // OldName may be a either a short or a full name.
+ Ref ref = repo.exactRef(oldName);
+ if (ref == null) {
+ ref = repo.exactRef(Constants.R_HEADS + oldName);
+ Ref ref2 = repo.exactRef(Constants.R_REMOTES + oldName);
+ if (ref != null && ref2 != null) {
+ throw new RefNotFoundException(MessageFormat.format(
+ JGitText.get().renameBranchFailedAmbiguous,
+ oldName, ref.getName(), ref2.getName()));
+ } else if (ref == null) {
+ if (ref2 != null) {
+ ref = ref2;
+ } else {
+ throw new RefNotFoundException(MessageFormat.format(
+ JGitText.get().refNotResolved, oldName));
+ }
+ }
+ }
fullOldName = ref.getName();
} else {
fullOldName = repo.getFullBranch();
@@ -124,26 +137,34 @@ public class RenameBranchCommand extends GitCommand<Ref> {
throw new DetachedHeadException();
}
- if (fullOldName.startsWith(Constants.R_REMOTES))
+ if (fullOldName.startsWith(Constants.R_REMOTES)) {
fullNewName = Constants.R_REMOTES + newName;
- else {
+ } else if (fullOldName.startsWith(Constants.R_HEADS)) {
fullNewName = Constants.R_HEADS + newName;
+ } else {
+ throw new RefNotFoundException(MessageFormat.format(
+ JGitText.get().renameBranchFailedNotABranch,
+ fullOldName));
}
- if (!Repository.isValidRefName(fullNewName))
+ if (!Repository.isValidRefName(fullNewName)) {
throw new InvalidRefNameException(MessageFormat.format(JGitText
.get().branchNameInvalid, fullNewName));
-
+ }
+ if (repo.exactRef(fullNewName) != null) {
+ throw new RefAlreadyExistsException(MessageFormat
+ .format(JGitText.get().refAlreadyExists1, fullNewName));
+ }
RefRename rename = repo.renameRef(fullOldName, fullNewName);
Result renameResult = rename.rename();
setCallable(false);
- if (Result.RENAMED != renameResult)
+ if (Result.RENAMED != renameResult) {
throw new JGitInternalException(MessageFormat.format(JGitText
.get().renameBranchUnexpectedResult, renameResult
.name()));
-
+ }
if (fullNewName.startsWith(Constants.R_HEADS)) {
String shortOldName = fullOldName.substring(Constants.R_HEADS
.length());
@@ -154,8 +175,9 @@ public class RenameBranchCommand extends GitCommand<Ref> {
String[] values = repoConfig.getStringList(
ConfigConstants.CONFIG_BRANCH_SECTION,
shortOldName, name);
- if (values.length == 0)
+ if (values.length == 0) {
continue;
+ }
// Keep any existing values already configured for the
// new branch name
String[] existing = repoConfig.getStringList(
@@ -180,10 +202,11 @@ public class RenameBranchCommand extends GitCommand<Ref> {
repoConfig.save();
}
- Ref resultRef = repo.findRef(newName);
- if (resultRef == null)
+ Ref resultRef = repo.exactRef(fullNewName);
+ if (resultRef == null) {
throw new JGitInternalException(
JGitText.get().renameBranchFailedUnknownReason);
+ }
return resultRef;
} catch (IOException ioe) {
throw new JGitInternalException(ioe.getMessage(), ioe);
@@ -191,7 +214,13 @@ public class RenameBranchCommand extends GitCommand<Ref> {
}
/**
- * Set the new name of the branch
+ * Sets the new short name of the branch.
+ * <p>
+ * The full name is constructed using the prefix of the branch to be renamed
+ * defined by either {@link #setOldName(String)} or HEAD. If that old branch
+ * is a local branch, the renamed branch also will be, and if the old branch
+ * is a remote branch, so will be the renamed branch.
+ * </p>
*
* @param newName
* the new name
@@ -204,7 +233,11 @@ public class RenameBranchCommand extends GitCommand<Ref> {
}
/**
- * Set the old name of the branch
+ * Sets the old name of the branch.
+ * <p>
+ * {@code oldName} may be a short or a full name. Using a full name is
+ * recommended to unambiguously identify the branch to be renamed.
+ * </p>
*
* @param oldName
* the name of the branch to rename; if not set, the currently
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
index ff7c4c64bc..aeb9395c1a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -96,9 +96,9 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
private String stashRef;
- private boolean applyIndex = true;
+ private boolean restoreIndex = true;
- private boolean applyUntracked = true;
+ private boolean restoreUntracked = true;
private boolean ignoreRepositoryState;
@@ -196,7 +196,7 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
.getParent(1));
ObjectId stashHeadCommit = stashCommit.getParent(0);
ObjectId untrackedCommit = null;
- if (applyUntracked && stashCommit.getParentCount() == 3)
+ if (restoreUntracked && stashCommit.getParentCount() == 3)
untrackedCommit = revWalk.parseCommit(stashCommit.getParent(2));
ResolveMerger merger = (ResolveMerger) strategy.newMerger(repo);
@@ -216,7 +216,7 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
dc, merger.getResultTreeId());
dco.setFailOnConflict(true);
dco.checkout(); // Ignoring failed deletes....
- if (applyIndex) {
+ if (restoreIndex) {
ResolveMerger ixMerger = (ResolveMerger) strategy
.newMerger(repo, true);
ixMerger.setCommitNames(new String[] { "stashed HEAD", //$NON-NLS-1$
@@ -277,9 +277,24 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
*
* @param applyIndex
* true (default) if the command should restore the index state
+ * @deprecated use {@link #setRestoreIndex} instead
*/
+ @Deprecated
public void setApplyIndex(boolean applyIndex) {
- this.applyIndex = applyIndex;
+ this.restoreIndex = applyIndex;
+ }
+
+ /**
+ * Whether to restore the index state
+ *
+ * @param restoreIndex
+ * true (default) if the command should restore the index state
+ * @return {@code this}
+ * @since 5.3
+ */
+ public StashApplyCommand setRestoreIndex(boolean restoreIndex) {
+ this.restoreIndex = restoreIndex;
+ return this;
}
/**
@@ -302,9 +317,24 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
* @param applyUntracked
* true (default) if the command should restore untracked files
* @since 3.4
+ * @deprecated use {@link #setRestoreUntracked} instead
*/
+ @Deprecated
public void setApplyUntracked(boolean applyUntracked) {
- this.applyUntracked = applyUntracked;
+ this.restoreUntracked = applyUntracked;
+ }
+
+ /**
+ * Whether the command should restore untracked files
+ *
+ * @param restoreUntracked
+ * true (default) if the command should restore untracked files
+ * @return {@code this}
+ * @since 5.3
+ */
+ public StashApplyCommand setRestoreUntracked(boolean restoreUntracked) {
+ this.restoreUntracked = restoreUntracked;
+ return this;
}
private void resetIndex(RevTree tree) throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/JGitInternalException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/JGitInternalException.java
index 57d8a13d10..c723da3e49 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/JGitInternalException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/JGitInternalException.java
@@ -44,7 +44,7 @@ package org.eclipse.jgit.api.errors;
* <p>
* During command execution a lot of exceptions may be thrown. Some of them
* represent error situations which can be handled specifically by the caller of
- * the command. But a lot of exceptions are so low-level that is is unlikely
+ * the command. But a lot of exceptions are so low-level that it is unlikely
* that the caller of the command can handle them effectively. The huge number
* of these low-level exceptions which are thrown by the commands lead to a
* complicated and wide interface of the commands. Callers of the API have to
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnsupportedSigningFormatException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnsupportedSigningFormatException.java
new file mode 100644
index 0000000000..eb5db6ad16
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnsupportedSigningFormatException.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018, Salesforce and
+ * other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v1.0 which accompanies this
+ * distribution, is reproduced below, and is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api.errors;
+
+/**
+ * Exception thrown when the configured gpg.format is not supported.
+ *
+ * @since 5.3
+ */
+public class UnsupportedSigningFormatException extends GitAPIException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor for UnsupportedGpgFormatException
+ *
+ * @param message
+ * error message
+ * @param cause
+ * a {@link java.lang.Throwable}
+ */
+ public UnsupportedSigningFormatException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructor for UnsupportedGpgFormatException
+ *
+ * @param message
+ * error message
+ */
+ public UnsupportedSigningFormatException(String message) {
+ super(message);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
index bd41d90680..6c0d90ebad 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
@@ -309,6 +309,74 @@ public class RawText extends Sequence {
}
/**
+ * Determine heuristically whether a byte array represents text content
+ * using CR-LF as line separator.
+ *
+ * @param raw
+ * the raw file content.
+ * @return {@code true} if raw is likely to be CR-LF delimited text,
+ * {@code false} otherwise
+ * @since 5.3
+ */
+ public static boolean isCrLfText(byte[] raw) {
+ return isCrLfText(raw, raw.length);
+ }
+
+ /**
+ * Determine heuristically whether the bytes contained in a stream represent
+ * text content using CR-LF as line separator.
+ *
+ * Note: Do not further use this stream after having called this method! The
+ * stream may not be fully read and will be left at an unknown position
+ * after consuming an unknown number of bytes. The caller is responsible for
+ * closing the stream.
+ *
+ * @param raw
+ * input stream containing the raw file content.
+ * @return {@code true} if raw is likely to be CR-LF delimited text,
+ * {@code false} otherwise
+ * @throws java.io.IOException
+ * if input stream could not be read
+ * @since 5.3
+ */
+ public static boolean isCrLfText(InputStream raw) throws IOException {
+ byte[] buffer = new byte[FIRST_FEW_BYTES];
+ int cnt = 0;
+ while (cnt < buffer.length) {
+ int n = raw.read(buffer, cnt, buffer.length - cnt);
+ if (n == -1) {
+ break;
+ }
+ cnt += n;
+ }
+ return isCrLfText(buffer, cnt);
+ }
+
+ /**
+ * Determine heuristically whether a byte array represents text content
+ * using CR-LF as line separator.
+ *
+ * @param raw
+ * the raw file content.
+ * @param length
+ * number of bytes in {@code raw} to evaluate.
+ * @return {@code true} if raw is likely to be CR-LF delimited text,
+ * {@code false} otherwise
+ * @since 5.3
+ */
+ public static boolean isCrLfText(byte[] raw, int length) {
+ boolean has_crlf = false;
+ for (int ptr = 0; ptr < length - 1; ptr++) {
+ if (raw[ptr] == '\0') {
+ return false; // binary
+ } else if (raw[ptr] == '\r' && raw[ptr + 1] == '\n') {
+ has_crlf = true;
+ }
+ }
+ return has_crlf;
+ }
+
+ /**
* Get the line delimiter for the first line.
*
* @since 2.0
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SequenceComparator.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SequenceComparator.java
index ccd0055585..3bc95a2f2f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SequenceComparator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SequenceComparator.java
@@ -84,7 +84,7 @@ public abstract class SequenceComparator<S extends Sequence> {
* method must produce the same integer result for both items.
*
* It is not required for two items to have different hash values if they
- * are are unequal according to the {@code equals()} method.
+ * are unequal according to the {@code equals()} method.
*
* @param seq
* the sequence.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
index 5897ffb758..539f2370e3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
@@ -236,7 +236,7 @@ public class SimilarityIndex {
* A region of a file is defined as a line in a text file or a fixed-size
* block in a binary file. To prepare an index, each region in the file is
* hashed; the values and counts of hashes are retained in a sorted table.
- * Define the similarity fraction F as the the count of matching regions
+ * Define the similarity fraction F as the count of matching regions
* between the two files divided between the maximum count of regions in
* either file. The similarity score is F multiplied by the maxScore
* constant, yielding a range [0, maxScore]. It is defined as maxScore for
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 8aa97df777..2d6228657a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -156,9 +156,11 @@ public class DirCacheCheckout {
private boolean failOnConflict = true;
+ private boolean force = false;
+
private ArrayList<String> toBeDeleted = new ArrayList<>();
- private boolean emptyDirCache;
+ private boolean initialCheckout;
private boolean performingCheckout;
@@ -231,7 +233,7 @@ public class DirCacheCheckout {
this.headCommitTree = headCommitTree;
this.mergeCommitTree = mergeCommitTree;
this.workingTree = workingTree;
- this.emptyDirCache = (dc == null) || (dc.getEntryCount() == 0);
+ this.initialCheckout = !repo.isBare() && !repo.getIndexFile().exists();
}
/**
@@ -430,11 +432,11 @@ public class DirCacheCheckout {
if (mtime == null || mtime.equals(Instant.EPOCH)) {
entry.setLastModified(f.getEntryLastModifiedInstant());
}
- keep(entry);
+ keep(entry, f);
}
} else
// The index contains a folder
- keep(i.getDirCacheEntry());
+ keep(i.getDirCacheEntry(), f);
} else {
// There is no entry in the merge commit. Means: we want to delete
// what's currently in the index and working tree
@@ -824,14 +826,14 @@ public class DirCacheCheckout {
break;
case 0xDFD: // 3 4
- keep(dce);
+ keep(dce, f);
break;
case 0xF0D: // 18
remove(name);
break;
case 0xDFF: // 5 5b 6 6b
if (equalIdAndMode(iId, iMode, mId, mMode))
- keep(dce); // 5 6
+ keep(dce, f); // 5 6
else
conflict(name, dce, h, m); // 5b 6b
break;
@@ -861,7 +863,7 @@ public class DirCacheCheckout {
conflict(name, dce, h, m); // 9
break;
case 0xFD0: // keep without a rule
- keep(dce);
+ keep(dce, f);
break;
case 0xFFD: // 12 13 14
if (equalIdAndMode(hId, hMode, iId, iMode))
@@ -881,7 +883,7 @@ public class DirCacheCheckout {
conflict(name, dce, h, m);
break;
default:
- keep(dce);
+ keep(dce, f);
}
return;
}
@@ -964,10 +966,10 @@ public class DirCacheCheckout {
// called before). Ignore the cached deletion and use what we
// find in Merge. Potentially updates the file.
if (equalIdAndMode(hId, hMode, mId, mMode)) {
- if (emptyDirCache)
+ if (initialCheckout)
update(name, mId, mMode);
else
- keep(dce);
+ keep(dce, f);
} else
conflict(name, dce, h, m);
}
@@ -1030,7 +1032,7 @@ public class DirCacheCheckout {
// Nothing in Head
// Something in Index
// -> Merge contains nothing new. Keep the index.
- keep(dce);
+ keep(dce, f);
} else
// Merge contains something and it is not the same as Index
// Nothing in Head
@@ -1179,7 +1181,7 @@ public class DirCacheCheckout {
// to the other one.
// -> In all three cases we don't touch index and file.
- keep(dce);
+ keep(dce, f);
}
}
}
@@ -1228,9 +1230,15 @@ public class DirCacheCheckout {
}
}
- private void keep(DirCacheEntry e) {
+ private void keep(DirCacheEntry e, WorkingTreeIterator f)
+ throws IOException {
if (e != null && !FileMode.TREE.equals(e.getFileMode()))
builder.add(e);
+ if (force) {
+ if (f.isModified(e, true, this.walk.getObjectReader())) {
+ checkoutEntry(repo, e, this.walk.getObjectReader());
+ }
+ }
}
private void remove(String path) {
@@ -1265,6 +1273,20 @@ public class DirCacheCheckout {
}
/**
+ * If <code>true</code>, dirty worktree files may be overridden. If
+ * <code>false</code> dirty worktree files will not be overridden in order
+ * not to delete unsaved content. This corresponds to native git's 'git
+ * checkout -f' option. By default this option is set to false.
+ *
+ * @param force
+ * a boolean.
+ * @since 5.3
+ */
+ public void setForce(boolean force) {
+ this.force = force;
+ }
+
+ /**
* This method implements how to handle conflicts when
* {@link #failOnConflict} is false
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index e9d86dfa83..cb62925a1f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -147,7 +147,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
* The URI of the remote repository
* @param ref
* Name of the ref to lookup. May be a short-hand form, e.g.
- * "master" which is is automatically expanded to
+ * "master" which is automatically expanded to
* "refs/heads/master" if "refs/heads/master" already exists.
* @return the sha1 of the remote repository, or null if the ref does
* not exist.
@@ -187,7 +187,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
* The URI of the remote repository
* @param ref
* Name of the ref to lookup. May be a short-hand form, e.g.
- * "master" which is is automatically expanded to
+ * "master" which is automatically expanded to
* "refs/heads/master" if "refs/heads/master" already exists.
* @param path
* The relative path (inside the repo) to the file to read
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 5bbfe81dff..039a6b2b43 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -286,6 +286,7 @@ public class JGitText extends TranslationBundle {
/***/ public String createNewFileFailed;
/***/ public String createRequiresZeroOldId;
/***/ public String credentialPassword;
+ /***/ public String credentialPassphrase;
/***/ public String credentialUsername;
/***/ public String daemonAlreadyRunning;
/***/ public String daysAgo;
@@ -387,6 +388,14 @@ public class JGitText extends TranslationBundle {
/***/ public String gcLogExists;
/***/ public String gcTooManyUnpruned;
/***/ public String gitmodulesNotFound;
+ /***/ public String gpgFailedToParseSecretKey;
+ /***/ public String gpgNoCredentialsProvider;
+ /***/ public String gpgNoKeyring;
+ /***/ public String gpgNoKeyInLegacySecring;
+ /***/ public String gpgNoPublicKeyFound;
+ /***/ public String gpgNoSecretKeyForPublicKey;
+ /***/ public String gpgKeyInfo;
+ /***/ public String gpgSigningCancelled;
/***/ public String headRequiredToStash;
/***/ public String hoursAgo;
/***/ public String httpConfigCannotNormalizeURL;
@@ -571,6 +580,7 @@ public class JGitText extends TranslationBundle {
/***/ public String onlyAlreadyUpToDateAndFastForwardMergesAreAvailable;
/***/ public String onlyOneFetchSupported;
/***/ public String onlyOneOperationCallPerConnectionIsSupported;
+ /***/ public String onlyOpenPgpSupportedForSigning;
/***/ public String openFilesMustBeAtLeast1;
/***/ public String openingConnection;
/***/ public String operationCanceled;
@@ -649,8 +659,9 @@ public class JGitText extends TranslationBundle {
/***/ public String remoteDoesNotHaveSpec;
/***/ public String remoteDoesNotSupportSmartHTTPPush;
/***/ public String remoteHungUpUnexpectedly;
- /***/ public String remoteNameCantBeNull;
- /***/ public String renameBranchFailedBecauseTag;
+ /***/ public String remoteNameCannotBeNull;
+ /***/ public String renameBranchFailedAmbiguous;
+ /***/ public String renameBranchFailedNotABranch;
/***/ public String renameBranchFailedUnknownReason;
/***/ public String renameBranchUnexpectedResult;
/***/ public String renameCancelled;
@@ -690,7 +701,7 @@ public class JGitText extends TranslationBundle {
/***/ public String sequenceTooLargeForDiffAlgorithm;
/***/ public String serviceNotEnabledNoName;
/***/ public String serviceNotPermitted;
- /***/ public String sha1CollisionDetected1;
+ /***/ public String sha1CollisionDetected;
/***/ public String shallowCommitsAlreadyInitialized;
/***/ public String shallowPacksRequireDepthWalk;
/***/ public String shortCompressedStreamAt;
@@ -786,6 +797,7 @@ public class JGitText extends TranslationBundle {
/***/ public String unableToRemovePath;
/***/ public String unableToStore;
/***/ public String unableToWrite;
+ /***/ public String unableToSignCommitNoSecretKey;
/***/ public String unauthorized;
/***/ public String underflowedReftableBlock;
/***/ public String unencodeableFile;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BlockBasedFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BlockBasedFile.java
index 58d12ab233..c7d6c584e4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BlockBasedFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BlockBasedFile.java
@@ -48,7 +48,6 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.MessageFormat;
-import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.PackInvalidException;
import org.eclipse.jgit.internal.storage.pack.PackExt;
@@ -133,19 +132,19 @@ abstract class BlockBasedFile {
}
DfsBlock getOrLoadBlock(long pos, DfsReader ctx) throws IOException {
- return cache.getOrLoad(this, pos, ctx, null);
+ try (LazyChannel c = new LazyChannel(ctx, desc, ext)) {
+ return cache.getOrLoad(this, pos, ctx, c);
+ }
}
- DfsBlock readOneBlock(long pos, DfsReader ctx,
- @Nullable ReadableChannel fileChannel) throws IOException {
+ DfsBlock readOneBlock(long pos, DfsReader ctx, ReadableChannel rc)
+ throws IOException {
if (invalid) {
throw new PackInvalidException(getFileName(), invalidatingCause);
}
ctx.stats.readBlock++;
long start = System.nanoTime();
- ReadableChannel rc = fileChannel != null ? fileChannel
- : ctx.db.openFile(desc, ext);
try {
int size = blockSize(rc);
pos = (pos / size) * size;
@@ -193,9 +192,6 @@ abstract class BlockBasedFile {
return new DfsBlock(key, pos, buf);
} finally {
- if (rc != fileChannel) {
- rc.close();
- }
ctx.stats.readBlockMicros += elapsedMicros(start);
}
}
@@ -211,4 +207,37 @@ abstract class BlockBasedFile {
static long elapsedMicros(long start) {
return (System.nanoTime() - start) / 1000L;
}
+
+ /**
+ * A supplier of readable channel that opens the channel lazily.
+ */
+ private static class LazyChannel
+ implements AutoCloseable, DfsBlockCache.ReadableChannelSupplier {
+ private final DfsReader ctx;
+ private final DfsPackDescription desc;
+ private final PackExt ext;
+
+ private ReadableChannel rc;
+
+ LazyChannel(DfsReader ctx, DfsPackDescription desc, PackExt ext) {
+ this.ctx = ctx;
+ this.desc = desc;
+ this.ext = ext;
+ }
+
+ @Override
+ public ReadableChannel get() throws IOException {
+ if (rc == null) {
+ rc = ctx.db.openFile(desc, ext);
+ }
+ return rc;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (rc != null) {
+ rc.close();
+ }
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
index 46879529b5..c6e2fae42f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
@@ -49,9 +49,9 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
import java.util.stream.LongStream;
-import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackExt;
@@ -128,9 +128,18 @@ public final class DfsBlockCache {
/** Hash bucket directory; entries are chained below. */
private final AtomicReferenceArray<HashEntry> table;
- /** Locks to prevent concurrent loads for same (PackFile,position). */
+ /**
+ * Locks to prevent concurrent loads for same (PackFile,position) block. The
+ * number of locks is {@link DfsBlockCacheConfig#getConcurrencyLevel()} to
+ * cap the overall concurrent block loads.
+ */
private final ReentrantLock[] loadLocks;
+ /**
+ * A separate pool of locks to prevent concurrent loads for same index or bitmap from PackFile.
+ */
+ private final ReentrantLock[] refLocks;
+
/** Maximum number of bytes the cache should hold. */
private final long maxBytes;
@@ -177,19 +186,30 @@ public final class DfsBlockCache {
/** Protects the clock and its related data. */
private final ReentrantLock clockLock;
+ /**
+ * A consumer of object reference lock wait time milliseconds. May be used to build a metric.
+ */
+ private final Consumer<Long> refLockWaitTime;
+
/** Current position of the clock. */
private Ref clockHand;
@SuppressWarnings("unchecked")
private DfsBlockCache(DfsBlockCacheConfig cfg) {
tableSize = tableSize(cfg);
- if (tableSize < 1)
+ if (tableSize < 1) {
throw new IllegalArgumentException(JGitText.get().tSizeMustBeGreaterOrEqual1);
+ }
table = new AtomicReferenceArray<>(tableSize);
loadLocks = new ReentrantLock[cfg.getConcurrencyLevel()];
- for (int i = 0; i < loadLocks.length; i++)
+ for (int i = 0; i < loadLocks.length; i++) {
loadLocks[i] = new ReentrantLock(true /* fair */);
+ }
+ refLocks = new ReentrantLock[cfg.getConcurrencyLevel()];
+ for (int i = 0; i < refLocks.length; i++) {
+ refLocks[i] = new ReentrantLock(true /* fair */);
+ }
maxBytes = cfg.getBlockLimit();
maxStreamThroughCache = (long) (maxBytes * cfg.getStreamRatio());
@@ -207,6 +227,8 @@ public final class DfsBlockCache {
statMiss = new AtomicReference<>(newCounters());
statEvict = new AtomicReference<>(newCounters());
liveBytes = new AtomicReference<>(newCounters());
+
+ refLockWaitTime = cfg.getRefLockWaitTimeConsumer();
}
boolean shouldCopyThroughCache(long length) {
@@ -333,15 +355,17 @@ public final class DfsBlockCache {
private static int tableSize(DfsBlockCacheConfig cfg) {
final int wsz = cfg.getBlockSize();
final long limit = cfg.getBlockLimit();
- if (wsz <= 0)
+ if (wsz <= 0) {
throw new IllegalArgumentException(JGitText.get().invalidWindowSize);
- if (limit < wsz)
+ }
+ if (limit < wsz) {
throw new IllegalArgumentException(JGitText.get().windowSizeMustBeLesserThanLimit);
+ }
return (int) Math.min(5 * (limit / wsz) / 2, Integer.MAX_VALUE);
}
/**
- * Lookup a cached object, creating and loading it if it doesn't exist.
+ * Look up a cached object, creating and loading it if it doesn't exist.
*
* @param file
* the pack that "contains" the cached object.
@@ -350,13 +374,13 @@ public final class DfsBlockCache {
* @param ctx
* current thread's reader.
* @param fileChannel
- * optional channel to read {@code pack}.
+ * supplier for channel to read {@code pack}.
* @return the object reference.
* @throws IOException
* the reference was not in the cache and could not be loaded.
*/
DfsBlock getOrLoad(BlockBasedFile file, long position, DfsReader ctx,
- @Nullable ReadableChannel fileChannel) throws IOException {
+ ReadableChannelSupplier fileChannel) throws IOException {
final long requestedPosition = position;
position = file.alignToBlock(position);
@@ -388,11 +412,13 @@ public final class DfsBlockCache {
getStat(statMiss, key).incrementAndGet();
boolean credit = true;
try {
- v = file.readOneBlock(requestedPosition, ctx, fileChannel);
+ v = file.readOneBlock(requestedPosition, ctx,
+ fileChannel.get());
credit = false;
} finally {
- if (credit)
+ if (credit) {
creditSpace(blockSize, key);
+ }
}
if (position != v.start) {
// The file discovered its blockSize and adjusted.
@@ -405,8 +431,9 @@ public final class DfsBlockCache {
ref.hot = true;
for (;;) {
HashEntry n = new HashEntry(clean(e2), ref);
- if (table.compareAndSet(slot, e2, n))
+ if (table.compareAndSet(slot, e2, n)) {
break;
+ }
e2 = table.get(slot);
}
addToClock(ref, blockSize - v.size());
@@ -416,8 +443,9 @@ public final class DfsBlockCache {
// If the block size changed from the default, it is possible the block
// that was loaded is the wrong block for the requested position.
- if (v.contains(file.key, requestedPosition))
+ if (v.contains(file.key, requestedPosition)) {
return v;
+ }
return getOrLoad(file, requestedPosition, ctx, fileChannel);
}
@@ -488,6 +516,63 @@ public final class DfsBlockCache {
put(v.stream, v.start, v.size(), v);
}
+ /**
+ * Look up a cached object, creating and loading it if it doesn't exist.
+ *
+ * @param key
+ * the stream key of the pack.
+ * @param loader
+ * the function to load the reference.
+ * @return the object reference.
+ * @throws IOException
+ * the reference was not in the cache and could not be loaded.
+ */
+ <T> Ref<T> getOrLoadRef(DfsStreamKey key, RefLoader<T> loader)
+ throws IOException {
+ int slot = slot(key, 0);
+ HashEntry e1 = table.get(slot);
+ Ref<T> ref = scanRef(e1, key, 0);
+ if (ref != null) {
+ getStat(statHit, key).incrementAndGet();
+ return ref;
+ }
+
+ ReentrantLock regionLock = lockForRef(key);
+ long lockStart = System.currentTimeMillis();
+ regionLock.lock();
+ try {
+ HashEntry e2 = table.get(slot);
+ if (e2 != e1) {
+ ref = scanRef(e2, key, 0);
+ if (ref != null) {
+ getStat(statHit, key).incrementAndGet();
+ return ref;
+ }
+ }
+
+ if (refLockWaitTime != null) {
+ refLockWaitTime.accept(
+ Long.valueOf(System.currentTimeMillis() - lockStart));
+ }
+ getStat(statMiss, key).incrementAndGet();
+ ref = loader.load();
+ ref.hot = true;
+ // Reserve after loading to get the size of the object
+ reserveSpace(ref.size, key);
+ for (;;) {
+ HashEntry n = new HashEntry(clean(e2), ref);
+ if (table.compareAndSet(slot, e2, n)) {
+ break;
+ }
+ e2 = table.get(slot);
+ }
+ addToClock(ref, 0);
+ } finally {
+ regionLock.unlock();
+ }
+ return ref;
+ }
+
<T> Ref<T> putRef(DfsStreamKey key, long size, T v) {
return put(key, 0, (int) Math.min(size, Integer.MAX_VALUE), v);
}
@@ -496,8 +581,9 @@ public final class DfsBlockCache {
int slot = slot(key, pos);
HashEntry e1 = table.get(slot);
Ref<T> ref = scanRef(e1, key, pos);
- if (ref != null)
+ if (ref != null) {
return ref;
+ }
reserveSpace(size, key);
ReentrantLock regionLock = lockFor(key, pos);
@@ -516,8 +602,9 @@ public final class DfsBlockCache {
ref.hot = true;
for (;;) {
HashEntry n = new HashEntry(clean(e2), ref);
- if (table.compareAndSet(slot, e2, n))
+ if (table.compareAndSet(slot, e2, n)) {
break;
+ }
e2 = table.get(slot);
}
addToClock(ref, 0);
@@ -534,10 +621,11 @@ public final class DfsBlockCache {
@SuppressWarnings("unchecked")
<T> T get(DfsStreamKey key, long position) {
T val = (T) scan(table.get(slot(key, position)), key, position);
- if (val == null)
+ if (val == null) {
getStat(statMiss, key).incrementAndGet();
- else
+ } else {
getStat(statHit, key).incrementAndGet();
+ }
return val;
}
@@ -546,21 +634,13 @@ public final class DfsBlockCache {
return r != null ? r.get() : null;
}
- <T> Ref<T> getRef(DfsStreamKey key) {
- Ref<T> r = scanRef(table.get(slot(key, 0)), key, 0);
- if (r != null)
- getStat(statHit, key).incrementAndGet();
- else
- getStat(statMiss, key).incrementAndGet();
- return r;
- }
-
@SuppressWarnings("unchecked")
private <T> Ref<T> scanRef(HashEntry n, DfsStreamKey key, long position) {
for (; n != null; n = n.next) {
Ref<T> r = n.ref;
- if (r.position == position && r.key.equals(key))
+ if (r.position == position && r.key.equals(key)) {
return r.get() != null ? r : null;
+ }
}
return null;
}
@@ -573,6 +653,10 @@ public final class DfsBlockCache {
return loadLocks[(hash(key.hash, position) >>> 1) % loadLocks.length];
}
+ private ReentrantLock lockForRef(DfsStreamKey key) {
+ return refLocks[(key.hash >>> 1) % refLocks.length];
+ }
+
private static AtomicLong[] newCounters() {
AtomicLong[] ret = new AtomicLong[PackExt.values().length];
for (int i = 0; i < ret.length; i++) {
@@ -613,8 +697,9 @@ public final class DfsBlockCache {
private static HashEntry clean(HashEntry top) {
while (top != null && top.ref.next == null)
top = top.next;
- if (top == null)
+ if (top == null) {
return null;
+ }
HashEntry n = clean(top.next);
return n == top.next ? top : new HashEntry(n, top.ref);
}
@@ -649,8 +734,9 @@ public final class DfsBlockCache {
T get() {
T v = value;
- if (v != null)
+ if (v != null) {
hot = true;
+ }
return v;
}
@@ -658,4 +744,21 @@ public final class DfsBlockCache {
return value != null;
}
}
+
+ @FunctionalInterface
+ interface RefLoader<T> {
+ Ref<T> load() throws IOException;
+ }
+
+ /**
+ * Supplier for readable channel
+ */
+ @FunctionalInterface
+ interface ReadableChannelSupplier {
+ /**
+ * @return ReadableChannel
+ * @throws IOException
+ */
+ ReadableChannel get() throws IOException;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
index dd7cb89c96..cd1fa5f78f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
@@ -51,6 +51,7 @@ import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONCURRENCY_LEVEL;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_RATIO;
import java.text.MessageFormat;
+import java.util.function.Consumer;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Config;
@@ -71,6 +72,8 @@ public class DfsBlockCacheConfig {
private double streamRatio;
private int concurrencyLevel;
+ private Consumer<Long> refLock;
+
/**
* Create a default configuration.
*/
@@ -194,6 +197,27 @@ public class DfsBlockCacheConfig {
}
/**
+ * Get the consumer of the object reference lock wait time in milliseconds.
+ *
+ * @return consumer of wait time in milliseconds.
+ */
+ public Consumer<Long> getRefLockWaitTimeConsumer() {
+ return refLock;
+ }
+
+ /**
+ * Set the consumer for lock wait time.
+ *
+ * @param c
+ * consumer of wait time in milliseconds.
+ * @return {@code this}
+ */
+ public DfsBlockCacheConfig setRefLockWaitTimeConsumer(Consumer<Long> c) {
+ refLock = c;
+ return this;
+ }
+
+ /**
* 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/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
index c131f9457e..be1387ed0c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
@@ -88,6 +88,8 @@ import org.eclipse.jgit.util.LongList;
* objects are similar.
*/
public final class DfsPackFile extends BlockBasedFile {
+ private static final int REC_SIZE = Constants.OBJECT_ID_LENGTH + 8;
+
/**
* Lock for initialization of {@link #index} and {@link #corruptObjects}.
* <p>
@@ -96,13 +98,13 @@ public final class DfsPackFile extends BlockBasedFile {
private final Object initLock = new Object();
/** Index mapping {@link ObjectId} to position within the pack stream. */
- private volatile DfsBlockCache.Ref<PackIndex> index;
+ private volatile PackIndex index;
/** Reverse version of {@link #index} mapping position to {@link ObjectId}. */
- private volatile DfsBlockCache.Ref<PackReverseIndex> reverseIndex;
+ private volatile PackReverseIndex reverseIndex;
/** Index of compressed bitmap mapping entire object graph. */
- private volatile DfsBlockCache.Ref<PackBitmapIndex> bitmapIndex;
+ private volatile PackBitmapIndex bitmapIndex;
/**
* Objects we have tried to read, and discovered to be corrupt.
@@ -148,15 +150,15 @@ public final class DfsPackFile extends BlockBasedFile {
* @return whether the pack index file is loaded and cached in memory.
*/
public boolean isIndexLoaded() {
- DfsBlockCache.Ref<PackIndex> idxref = index;
- return idxref != null && idxref.has();
+ return index != null;
}
void setPackIndex(PackIndex idx) {
long objCnt = idx.getObjectCount();
int recSize = Constants.OBJECT_ID_LENGTH + 8;
long sz = objCnt * recSize;
- index = cache.putRef(desc.getStreamKey(INDEX), sz, idx);
+ cache.putRef(desc.getStreamKey(INDEX), sz, idx);
+ index = idx;
}
/**
@@ -174,11 +176,8 @@ public final class DfsPackFile extends BlockBasedFile {
}
private PackIndex idx(DfsReader ctx) throws IOException {
- DfsBlockCache.Ref<PackIndex> idxref = index;
- if (idxref != null) {
- PackIndex idx = idxref.get();
- if (idx != null)
- return idx;
+ if (index != null) {
+ return index;
}
if (invalid) {
@@ -189,56 +188,61 @@ public final class DfsPackFile extends BlockBasedFile {
.dispatch(new BeforeDfsPackIndexLoadedEvent(this));
synchronized (initLock) {
- idxref = index;
- if (idxref != null) {
- PackIndex idx = idxref.get();
- if (idx != null)
- return idx;
- }
-
- DfsStreamKey idxKey = desc.getStreamKey(INDEX);
- idxref = cache.getRef(idxKey);
- if (idxref != null) {
- PackIndex idx = idxref.get();
- if (idx != null) {
- index = idxref;
- return idx;
- }
+ if (index != null) {
+ return index;
}
- PackIndex idx;
try {
- ctx.stats.readIdx++;
- long start = System.nanoTime();
- try (ReadableChannel rc = ctx.db.openFile(desc, INDEX)) {
- InputStream in = Channels.newInputStream(rc);
- int wantSize = 8192;
- int bs = rc.blockSize();
- if (0 < bs && bs < wantSize)
- bs = (wantSize / bs) * bs;
- else if (bs <= 0)
- bs = wantSize;
- idx = PackIndex.read(new BufferedInputStream(in, bs));
- ctx.stats.readIdxBytes += rc.position();
- } finally {
- ctx.stats.readIdxMicros += elapsedMicros(start);
+ DfsStreamKey idxKey = desc.getStreamKey(INDEX);
+ DfsBlockCache.Ref<PackIndex> idxref = cache.getOrLoadRef(idxKey,
+ () -> {
+ try {
+ ctx.stats.readIdx++;
+ long start = System.nanoTime();
+ try (ReadableChannel rc = ctx.db.openFile(desc,
+ INDEX)) {
+ InputStream in = Channels
+ .newInputStream(rc);
+ int wantSize = 8192;
+ int bs = rc.blockSize();
+ if (0 < bs && bs < wantSize) {
+ bs = (wantSize / bs) * bs;
+ } else if (bs <= 0) {
+ bs = wantSize;
+ }
+ PackIndex idx = PackIndex.read(
+ new BufferedInputStream(in, bs));
+ int sz = (int) Math.min(
+ idx.getObjectCount() * REC_SIZE,
+ Integer.MAX_VALUE);
+ ctx.stats.readIdxBytes += rc.position();
+ index = idx;
+ return new DfsBlockCache.Ref<>(idxKey, 0,
+ sz, idx);
+ } finally {
+ ctx.stats.readIdxMicros += elapsedMicros(
+ start);
+ }
+ } catch (EOFException e) {
+ throw new IOException(MessageFormat.format(
+ DfsText.get().shortReadOfIndex,
+ desc.getFileName(INDEX)), e);
+ } catch (IOException e) {
+ throw new IOException(MessageFormat.format(
+ DfsText.get().cannotReadIndex,
+ desc.getFileName(INDEX)), e);
+ }
+ });
+ PackIndex idx = idxref.get();
+ if (index == null && idx != null) {
+ index = idx;
}
- } catch (EOFException e) {
- invalid = true;
- invalidatingCause = e;
- throw new IOException(MessageFormat.format(
- DfsText.get().shortReadOfIndex,
- desc.getFileName(INDEX)), e);
+ return index;
} catch (IOException e) {
invalid = true;
invalidatingCause = e;
- throw new IOException(MessageFormat.format(
- DfsText.get().cannotReadIndex,
- desc.getFileName(INDEX)), e);
+ throw e;
}
-
- setPackIndex(idx);
- return idx;
}
}
@@ -247,102 +251,94 @@ public final class DfsPackFile extends BlockBasedFile {
}
PackBitmapIndex getBitmapIndex(DfsReader ctx) throws IOException {
- if (invalid || isGarbage() || !desc.hasFileExt(BITMAP_INDEX))
+ if (invalid || isGarbage() || !desc.hasFileExt(BITMAP_INDEX)) {
return null;
+ }
- DfsBlockCache.Ref<PackBitmapIndex> idxref = bitmapIndex;
- if (idxref != null) {
- PackBitmapIndex idx = idxref.get();
- if (idx != null)
- return idx;
+ if (bitmapIndex != null) {
+ return bitmapIndex;
}
synchronized (initLock) {
- idxref = bitmapIndex;
- if (idxref != null) {
- PackBitmapIndex idx = idxref.get();
- if (idx != null)
- return idx;
+ if (bitmapIndex != null) {
+ return bitmapIndex;
}
+ PackIndex idx = idx(ctx);
+ PackReverseIndex revidx = getReverseIdx(ctx);
DfsStreamKey bitmapKey = desc.getStreamKey(BITMAP_INDEX);
- idxref = cache.getRef(bitmapKey);
- if (idxref != null) {
- PackBitmapIndex idx = idxref.get();
- if (idx != null) {
- bitmapIndex = idxref;
- return idx;
- }
- }
-
- long size;
- PackBitmapIndex idx;
- ctx.stats.readBitmap++;
- long start = System.nanoTime();
- try (ReadableChannel rc = ctx.db.openFile(desc, BITMAP_INDEX)) {
- try {
- InputStream in = Channels.newInputStream(rc);
- int wantSize = 8192;
- int bs = rc.blockSize();
- if (0 < bs && bs < wantSize)
- bs = (wantSize / bs) * bs;
- else if (bs <= 0)
- bs = wantSize;
- in = new BufferedInputStream(in, bs);
- idx = PackBitmapIndex.read(
- in, idx(ctx), getReverseIdx(ctx));
- } finally {
- size = rc.position();
- ctx.stats.readIdxBytes += size;
- ctx.stats.readIdxMicros += elapsedMicros(start);
- }
- } catch (EOFException e) {
- throw new IOException(MessageFormat.format(
- DfsText.get().shortReadOfIndex,
- desc.getFileName(BITMAP_INDEX)), e);
- } catch (IOException e) {
- throw new IOException(MessageFormat.format(
- DfsText.get().cannotReadIndex,
- desc.getFileName(BITMAP_INDEX)), e);
+ DfsBlockCache.Ref<PackBitmapIndex> idxref = cache
+ .getOrLoadRef(bitmapKey, () -> {
+ ctx.stats.readBitmap++;
+ long start = System.nanoTime();
+ try (ReadableChannel rc = ctx.db.openFile(desc,
+ BITMAP_INDEX)) {
+ long size;
+ PackBitmapIndex bmidx;
+ try {
+ InputStream in = Channels.newInputStream(rc);
+ int wantSize = 8192;
+ int bs = rc.blockSize();
+ if (0 < bs && bs < wantSize) {
+ bs = (wantSize / bs) * bs;
+ } else if (bs <= 0) {
+ bs = wantSize;
+ }
+ in = new BufferedInputStream(in, bs);
+ bmidx = PackBitmapIndex.read(in, idx, revidx);
+ } finally {
+ size = rc.position();
+ ctx.stats.readIdxBytes += size;
+ ctx.stats.readIdxMicros += elapsedMicros(start);
+ }
+ int sz = (int) Math.min(size, Integer.MAX_VALUE);
+ bitmapIndex = bmidx;
+ return new DfsBlockCache.Ref<>(bitmapKey, 0, sz,
+ bmidx);
+ } catch (EOFException e) {
+ throw new IOException(MessageFormat.format(
+ DfsText.get().shortReadOfIndex,
+ desc.getFileName(BITMAP_INDEX)), e);
+ } catch (IOException e) {
+ throw new IOException(MessageFormat.format(
+ DfsText.get().cannotReadIndex,
+ desc.getFileName(BITMAP_INDEX)), e);
+ }
+ });
+ PackBitmapIndex bmidx = idxref.get();
+ if (bitmapIndex == null && bmidx != null) {
+ bitmapIndex = bmidx;
}
-
- bitmapIndex = cache.putRef(bitmapKey, size, idx);
- return idx;
+ return bitmapIndex;
}
}
PackReverseIndex getReverseIdx(DfsReader ctx) throws IOException {
- DfsBlockCache.Ref<PackReverseIndex> revref = reverseIndex;
- if (revref != null) {
- PackReverseIndex revidx = revref.get();
- if (revidx != null)
- return revidx;
+ if (reverseIndex != null) {
+ return reverseIndex;
}
synchronized (initLock) {
- revref = reverseIndex;
- if (revref != null) {
- PackReverseIndex revidx = revref.get();
- if (revidx != null)
- return revidx;
- }
-
- DfsStreamKey revKey =
- new DfsStreamKey.ForReverseIndex(desc.getStreamKey(INDEX));
- revref = cache.getRef(revKey);
- if (revref != null) {
- PackReverseIndex idx = revref.get();
- if (idx != null) {
- reverseIndex = revref;
- return idx;
- }
+ if (reverseIndex != null) {
+ return reverseIndex;
}
PackIndex idx = idx(ctx);
- PackReverseIndex revidx = new PackReverseIndex(idx);
- long cnt = idx.getObjectCount();
- reverseIndex = cache.putRef(revKey, cnt * 8, revidx);
- return revidx;
+ DfsStreamKey revKey = new DfsStreamKey.ForReverseIndex(
+ desc.getStreamKey(INDEX));
+ DfsBlockCache.Ref<PackReverseIndex> revref = cache
+ .getOrLoadRef(revKey, () -> {
+ PackReverseIndex revidx = new PackReverseIndex(idx);
+ int sz = (int) Math.min(idx.getObjectCount() * 8,
+ Integer.MAX_VALUE);
+ reverseIndex = revidx;
+ return new DfsBlockCache.Ref<>(revKey, 0, sz, revidx);
+ });
+ PackReverseIndex revidx = revref.get();
+ if (reverseIndex == null && revidx != null) {
+ reverseIndex = revidx;
+ }
+ return reverseIndex;
}
}
@@ -420,110 +416,94 @@ public final class DfsPackFile extends BlockBasedFile {
return null;
}
- if (ctx.inflate(this, position, dstbuf, false) != sz)
+ if (ctx.inflate(this, position, dstbuf, false) != sz) {
throw new EOFException(MessageFormat.format(
JGitText.get().shortCompressedStreamAt,
Long.valueOf(position)));
+ }
return dstbuf;
}
- void copyPackAsIs(PackOutputStream out, DfsReader ctx)
- throws IOException {
+ void copyPackAsIs(PackOutputStream out, DfsReader ctx) throws IOException {
// If the length hasn't been determined yet, pin to set it.
if (length == -1) {
ctx.pin(this, 0);
ctx.unpin();
}
- if (cache.shouldCopyThroughCache(length))
- copyPackThroughCache(out, ctx);
- else
- copyPackBypassCache(out, ctx);
+ try (ReadableChannel rc = ctx.db.openFile(desc, PACK)) {
+ int sz = ctx.getOptions().getStreamPackBufferSize();
+ if (sz > 0) {
+ rc.setReadAheadBytes(sz);
+ }
+ if (cache.shouldCopyThroughCache(length)) {
+ copyPackThroughCache(out, ctx, rc);
+ } else {
+ copyPackBypassCache(out, rc);
+ }
+ }
}
- private void copyPackThroughCache(PackOutputStream out, DfsReader ctx)
- throws IOException {
- @SuppressWarnings("resource") // Explicitly closed in finally block
- ReadableChannel rc = null;
- try {
- long position = 12;
- long remaining = length - (12 + 20);
- while (0 < remaining) {
- DfsBlock b;
- if (rc != null) {
- b = cache.getOrLoad(this, position, ctx, rc);
- } else {
- b = cache.get(key, alignToBlock(position));
- if (b == null) {
- rc = ctx.db.openFile(desc, PACK);
- int sz = ctx.getOptions().getStreamPackBufferSize();
- if (sz > 0) {
- rc.setReadAheadBytes(sz);
- }
- b = cache.getOrLoad(this, position, ctx, rc);
- }
- }
-
- int ptr = (int) (position - b.start);
- int n = (int) Math.min(b.size() - ptr, remaining);
- b.write(out, position, n);
- position += n;
- remaining -= n;
- }
- } finally {
- if (rc != null) {
- rc.close();
+ private void copyPackThroughCache(PackOutputStream out, DfsReader ctx,
+ ReadableChannel rc) throws IOException {
+ long position = 12;
+ long remaining = length - (12 + 20);
+ while (0 < remaining) {
+ DfsBlock b = cache.getOrLoad(this, position, ctx, () -> rc);
+ int ptr = (int) (position - b.start);
+ if (b.size() <= ptr) {
+ throw packfileIsTruncated();
}
+ int n = (int) Math.min(b.size() - ptr, remaining);
+ b.write(out, position, n);
+ position += n;
+ remaining -= n;
}
}
- private long copyPackBypassCache(PackOutputStream out, DfsReader ctx)
+ private long copyPackBypassCache(PackOutputStream out, ReadableChannel rc)
throws IOException {
- try (ReadableChannel rc = ctx.db.openFile(desc, PACK)) {
- ByteBuffer buf = newCopyBuffer(out, rc);
- if (ctx.getOptions().getStreamPackBufferSize() > 0)
- rc.setReadAheadBytes(ctx.getOptions().getStreamPackBufferSize());
- long position = 12;
- long remaining = length - (12 + 20);
- boolean packHeadSkipped = false;
- while (0 < remaining) {
- DfsBlock b = cache.get(key, alignToBlock(position));
- if (b != null) {
- int ptr = (int) (position - b.start);
- int n = (int) Math.min(b.size() - ptr, remaining);
- b.write(out, position, n);
- position += n;
- remaining -= n;
- rc.position(position);
- packHeadSkipped = true;
- continue;
- }
-
- buf.position(0);
- int n = read(rc, buf);
- if (n <= 0)
+ ByteBuffer buf = newCopyBuffer(out, rc);
+ long position = 12;
+ long remaining = length - (12 + 20);
+ boolean packHeadSkipped = false;
+ while (0 < remaining) {
+ DfsBlock b = cache.get(key, alignToBlock(position));
+ if (b != null) {
+ int ptr = (int) (position - b.start);
+ if (b.size() <= ptr) {
throw packfileIsTruncated();
- else if (n > remaining)
- n = (int) remaining;
-
- if (!packHeadSkipped) {
- // Need skip the 'PACK' header for the first read
- out.write(buf.array(), 12, n - 12);
- packHeadSkipped = true;
- } else {
- out.write(buf.array(), 0, n);
}
+ int n = (int) Math.min(b.size() - ptr, remaining);
+ b.write(out, position, n);
position += n;
remaining -= n;
+ rc.position(position);
+ packHeadSkipped = true;
+ continue;
}
- return position;
+
+ // Need to skip the 'PACK' header for the first read
+ int ptr = packHeadSkipped ? 0 : 12;
+ buf.position(0);
+ int bufLen = read(rc, buf);
+ if (bufLen <= ptr) {
+ throw packfileIsTruncated();
+ }
+ int n = (int) Math.min(bufLen - ptr, remaining);
+ out.write(buf.array(), ptr, n);
+ position += n;
+ remaining -= n;
+ packHeadSkipped = true;
}
+ return position;
}
private ByteBuffer newCopyBuffer(PackOutputStream out, ReadableChannel rc) {
int bs = blockSize(rc);
byte[] copyBuf = out.getCopyBuffer();
- if (bs > copyBuf.length)
+ if (bs > copyBuf.length) {
copyBuf = new byte[bs];
+ }
return ByteBuffer.wrap(copyBuf, 0, bs);
}
@@ -635,8 +615,9 @@ public final class DfsPackFile extends BlockBasedFile {
readFully(pos, buf, 0, n, ctx);
crc1.update(buf, 0, n);
inf.setInput(buf, 0, n);
- while (inf.inflate(tmp, 0, tmp.length) > 0)
+ while (inf.inflate(tmp, 0, tmp.length) > 0) {
continue;
+ }
pos += n;
cnt -= n;
}
@@ -770,8 +751,9 @@ public final class DfsPackFile extends BlockBasedFile {
if (sz < ctx.getStreamFileThreshold()) {
data = decompress(pos + p, (int) sz, ctx);
- if (data != null)
+ if (data != null) {
return new ObjectLoader.SmallObject(typeCode, data);
+ }
}
return new LargePackedWholeObject(typeCode, sz, pos, p, this, ctx.db);
}
@@ -787,8 +769,9 @@ public final class DfsPackFile extends BlockBasedFile {
}
base = pos - base;
delta = new Delta(delta, pos, (int) sz, p, base);
- if (sz != delta.deltaSize)
+ if (sz != delta.deltaSize) {
break SEARCH;
+ }
DeltaBaseCache.Entry e = ctx.getDeltaBaseCache().get(key, base);
if (e != null) {
@@ -805,8 +788,9 @@ public final class DfsPackFile extends BlockBasedFile {
readFully(pos + p, ib, 0, 20, ctx);
long base = findDeltaBase(ctx, ObjectId.fromRaw(ib));
delta = new Delta(delta, pos, (int) sz, p + 20, base);
- if (sz != delta.deltaSize)
+ if (sz != delta.deltaSize) {
break SEARCH;
+ }
DeltaBaseCache.Entry e = ctx.getDeltaBaseCache().get(key, base);
if (e != null) {
@@ -834,10 +818,11 @@ public final class DfsPackFile extends BlockBasedFile {
assert(delta != null);
do {
// Cache only the base immediately before desired object.
- if (cached)
+ if (cached) {
cached = false;
- else if (delta.next == null)
+ } else if (delta.next == null) {
ctx.getDeltaBaseCache().put(key, delta.basePos, type, data);
+ }
pos = delta.deltaPos;
@@ -848,8 +833,9 @@ public final class DfsPackFile extends BlockBasedFile {
}
final long sz = BinaryDelta.getResultSize(cmds);
- if (Integer.MAX_VALUE <= sz)
+ if (Integer.MAX_VALUE <= sz) {
throw new LargeObjectException.ExceedsByteArrayLimit();
+ }
final byte[] result;
try {
@@ -879,9 +865,10 @@ public final class DfsPackFile extends BlockBasedFile {
private long findDeltaBase(DfsReader ctx, ObjectId baseId)
throws IOException, MissingObjectException {
long ofs = idx(ctx).findOffset(baseId);
- if (ofs < 0)
+ if (ofs < 0) {
throw new MissingObjectException(baseId,
JGitText.get().missingDeltaBase);
+ }
return ofs;
}
@@ -938,8 +925,9 @@ public final class DfsPackFile extends BlockBasedFile {
case Constants.OBJ_OFS_DELTA: {
int p = 1;
- while ((c & 0x80) != 0)
+ while ((c & 0x80) != 0) {
c = ib[p++] & 0xff;
+ }
c = ib[p++] & 0xff;
long ofs = c & 127;
while ((c & 128) != 0) {
@@ -954,8 +942,9 @@ public final class DfsPackFile extends BlockBasedFile {
case Constants.OBJ_REF_DELTA: {
int p = 1;
- while ((c & 0x80) != 0)
+ while ((c & 0x80) != 0) {
c = ib[p++] & 0xff;
+ }
readFully(pos + p, ib, 0, 20, ctx);
pos = findDeltaBase(ctx, ObjectId.fromRaw(ib));
continue;
@@ -998,8 +987,9 @@ public final class DfsPackFile extends BlockBasedFile {
case Constants.OBJ_OFS_DELTA:
c = ib[p++] & 0xff;
- while ((c & 128) != 0)
+ while ((c & 128) != 0) {
c = ib[p++] & 0xff;
+ }
deltaAt = pos + p;
break;
@@ -1032,8 +1022,9 @@ public final class DfsPackFile extends BlockBasedFile {
int c = ib[0] & 0xff;
int p = 1;
final int typeCode = (c >> 4) & 7;
- while ((c & 0x80) != 0)
+ while ((c & 0x80) != 0) {
c = ib[p++] & 0xff;
+ }
long len = rev.findNextOffset(pos, length - 20) - pos;
switch (typeCode) {
@@ -1077,8 +1068,9 @@ public final class DfsPackFile extends BlockBasedFile {
boolean isCorrupt(long offset) {
LongList list = corruptObjects;
- if (list == null)
+ if (list == null) {
return false;
+ }
synchronized (list) {
return list.contains(offset);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
index a884346842..8b2a03d4c5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
@@ -107,20 +107,6 @@ public abstract class DfsRefDatabase extends RefDatabase {
/** {@inheritDoc} */
@Override
- public Ref getRef(String needle) throws IOException {
- RefCache curr = read();
- for (String prefix : SEARCH_PATH) {
- Ref ref = curr.ids.get(prefix + needle);
- if (ref != null) {
- ref = resolve(ref, 0, curr.ids);
- return ref;
- }
- }
- return null;
- }
-
- /** {@inheritDoc} */
- @Override
public List<Ref> getAdditionalRefs() {
return Collections.emptyList();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftable.java
index 7502471b0c..4853298012 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftable.java
@@ -128,7 +128,7 @@ public class DfsReftable extends BlockBasedFile {
open().setReadAheadBytes(readAhead);
}
- DfsBlock block = cache.getOrLoad(file, pos, ctx, ch);
+ DfsBlock block = cache.getOrLoad(file, pos, ctx, () -> open());
if (block.start == pos && block.size() >= cnt) {
return block.zeroCopyByteBuffer(cnt);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
index 70816307f5..83394bb92c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
@@ -99,6 +99,12 @@ public class DfsReftableDatabase extends DfsRefDatabase {
/** {@inheritDoc} */
@Override
+ public boolean hasVersioning() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
public boolean performsAtomicTransactions() {
return true;
}
@@ -223,18 +229,6 @@ public class DfsReftableDatabase extends DfsRefDatabase {
/** {@inheritDoc} */
@Override
- public Ref getRef(String needle) throws IOException {
- for (String prefix : SEARCH_PATH) {
- Ref ref = exactRef(prefix + needle);
- if (ref != null) {
- return ref;
- }
- }
- return null;
- }
-
- /** {@inheritDoc} */
- @Override
public Map<String, Ref> getRefs(String prefix) throws IOException {
RefList.Builder<Ref> all = new RefList.Builder<>();
lock.lock();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index 791a108289..7400308c86 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -160,7 +160,6 @@ public class GC {
*
* @param e
* the executor to be used for running auto-gc
- * @since 4.8
*/
public static void setExecutor(ExecutorService e) {
executor = e;
@@ -918,7 +917,8 @@ public class GC {
// Avoid deleting a folder that was created after the threshold so that concurrent
// operations trying to create a reference are not impacted
Instant threshold = Instant.now().minus(30, ChronoUnit.SECONDS);
- try (Stream<Path> entries = Files.list(refs)) {
+ try (Stream<Path> entries = Files.list(refs)
+ .filter(Files::isDirectory)) {
Iterator<Path> iterator = entries.iterator();
while (iterator.hasNext()) {
try (Stream<Path> s = Files.list(iterator.next())) {
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 35522667e0..258cceebee 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
@@ -198,7 +198,6 @@ public class ObjectDirectory extends FileObjectDatabase {
* <p>Getter for the field <code>packDirectory</code>.</p>
*
* @return the location of the <code>pack</code> directory.
- * @since 4.10
*/
public final File getPackDirectory() {
return packDirectory;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index de7e4b3f25..a4729bba48 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -74,6 +74,7 @@ import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -319,16 +320,14 @@ public class RefDirectory extends RefDatabase {
return loose;
}
- /** {@inheritDoc} */
- @Override
- public Ref exactRef(String name) throws IOException {
- RefList<Ref> packed = getPackedRefs();
- Ref ref;
+ @Nullable
+ private Ref readAndResolve(String name, RefList<Ref> packed) throws IOException {
try {
- ref = readRef(name, packed);
+ Ref ref = readRef(name, packed);
if (ref != null) {
ref = resolve(ref, 0, null, null, packed);
}
+ return ref;
} catch (IOException e) {
if (name.contains("/") //$NON-NLS-1$
|| !(e.getCause() instanceof InvalidObjectIdException)) {
@@ -338,35 +337,55 @@ public class RefDirectory extends RefDatabase {
// While looking for a ref outside of refs/ (e.g., 'config'), we
// found a non-ref file (e.g., a config file) instead. Treat this
// as a ref-not-found condition.
- ref = null;
+ return null;
}
- fireRefsChanged();
- return ref;
}
/** {@inheritDoc} */
@Override
- public Ref getRef(String needle) throws IOException {
- final RefList<Ref> packed = getPackedRefs();
- Ref ref = null;
- for (String prefix : SEARCH_PATH) {
- try {
- ref = readRef(prefix + needle, packed);
+ public Ref exactRef(String name) throws IOException {
+ try {
+ return readAndResolve(name, getPackedRefs());
+ } finally {
+ fireRefsChanged();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @NonNull
+ public Map<String, Ref> exactRef(String... refs) throws IOException {
+ try {
+ RefList<Ref> packed = getPackedRefs();
+ Map<String, Ref> result = new HashMap<>(refs.length);
+ for (String name : refs) {
+ Ref ref = readAndResolve(name, packed);
if (ref != null) {
- ref = resolve(ref, 0, null, null, packed);
+ result.put(name, ref);
}
+ }
+ return result;
+ } finally {
+ fireRefsChanged();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable
+ public Ref firstExactRef(String... refs) throws IOException {
+ try {
+ RefList<Ref> packed = getPackedRefs();
+ for (String name : refs) {
+ Ref ref = readAndResolve(name, packed);
if (ref != null) {
- break;
- }
- } catch (IOException e) {
- if (!(!needle.contains("/") && "".equals(prefix) && e //$NON-NLS-1$ //$NON-NLS-2$
- .getCause() instanceof InvalidObjectIdException)) {
- throw e;
+ return ref;
}
}
+ return null;
+ } finally {
+ fireRefsChanged();
}
- fireRefsChanged();
- return ref;
}
/** {@inheritDoc} */
@@ -414,7 +433,7 @@ public class RefDirectory extends RefDatabase {
public List<Ref> getAdditionalRefs() throws IOException {
List<Ref> ret = new LinkedList<>();
for (String name : additionalRefsNames) {
- Ref r = getRef(name);
+ Ref r = exactRef(name);
if (r != null)
ret.add(r);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
index 45ce6349a5..1a0d6953ab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
@@ -87,7 +87,7 @@ class RefDirectoryUpdate extends RefUpdate {
String name = dst.getName();
lock = new LockFile(database.fileFor(name));
if (lock.lock()) {
- dst = database.getRef(name);
+ dst = database.findRef(name);
setOldObjectId(dst != null ? dst.getObjectId() : null);
return true;
} else {
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 8cf1d4e219..e8fac514be 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
@@ -146,7 +146,7 @@ public class WindowCache {
* Modify the configuration of the window cache.
* <p>
* The new configuration is applied immediately. If the new limits are
- * smaller than what what is currently cached, older entries will be purged
+ * smaller than what is currently cached, older entries will be purged
* as soon as possible to allow the cache to meet the new limit.
*
* @deprecated use {@code cfg.install()} to avoid internal reference.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaEncoder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaEncoder.java
index 343faf4df4..cfc1ccd625 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaEncoder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaEncoder.java
@@ -73,7 +73,7 @@ public class DeltaEncoder {
/** Maximum number of bytes used by a copy instruction. */
private static final int MAX_COPY_CMD_SIZE = 8;
- /** Maximum length that an an insert command can encode at once. */
+ /** Maximum length that an insert command can encode at once. */
private static final int MAX_INSERT_DATA_SIZE = 127;
private final OutputStream out;
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 24af8a73ba..1e3d74ab57 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
@@ -642,7 +642,6 @@ public class PackWriter implements AutoCloseable {
/**
* @param bytes exclude blobs of size greater than this
- * @since 5.0
*/
public void setFilterBlobLimit(long bytes) {
filterBlobLimit = bytes;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
index ce2ba4a2e1..44529bfff2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
@@ -170,24 +170,27 @@ class BlockReader {
return readVarint64();
}
- Ref readRef() throws IOException {
+ Ref readRef(long minUpdateIndex) throws IOException {
+ long updateIndex = minUpdateIndex + readUpdateIndexDelta();
String name = RawParseUtils.decode(UTF_8, nameBuf, 0, nameLen);
switch (valueType & VALUE_TYPE_MASK) {
case VALUE_NONE: // delete
- return newRef(name);
+ return newRef(name, updateIndex);
case VALUE_1ID:
- return new ObjectIdRef.PeeledNonTag(PACKED, name, readValueId());
+ return new ObjectIdRef.PeeledNonTag(PACKED, name, readValueId(),
+ updateIndex);
case VALUE_2ID: { // annotated tag
ObjectId id1 = readValueId();
ObjectId id2 = readValueId();
- return new ObjectIdRef.PeeledTag(PACKED, name, id1, id2);
+ return new ObjectIdRef.PeeledTag(PACKED, name, id1, id2,
+ updateIndex);
}
case VALUE_SYMREF: {
String val = readValueString();
- return new SymbolicRef(name, newRef(val));
+ return new SymbolicRef(name, newRef(val, updateIndex), updateIndex);
}
default:
@@ -410,7 +413,7 @@ class BlockReader {
* <ul>
* <li>{@link #name()}
* <li>{@link #match(byte[], boolean)}
- * <li>{@link #readRef()}
+ * <li>{@link #readRef(long)}
* <li>{@link #readLogUpdateIndex()}
* <li>{@link #readLogEntry()}
* <li>{@link #readBlockPositionList()}
@@ -575,8 +578,8 @@ class BlockReader {
return val;
}
- private static Ref newRef(String name) {
- return new ObjectIdRef.Unpeeled(NEW, name, null);
+ private static Ref newRef(String name, long updateIndex) {
+ return new ObjectIdRef.Unpeeled(NEW, name, null, updateIndex);
}
private static IOException invalidBlock() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java
index 17894b1664..c740bf2c37 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java
@@ -168,7 +168,6 @@ public class MergedReftable extends Reftable {
private final PriorityQueue<RefQueueEntry> queue;
private RefQueueEntry head;
private Ref ref;
- private long updateIndex;
MergedRefCursor() {
queue = new PriorityQueue<>(queueSize(), RefQueueEntry::compare);
@@ -206,7 +205,6 @@ public class MergedReftable extends Reftable {
}
ref = t.rc.getRef();
- updateIndex = t.rc.getUpdateIndex();
boolean include = includeDeletes || !t.rc.wasDeleted();
add(t);
skipShadowedRefs(ref.getName());
@@ -242,11 +240,6 @@ public class MergedReftable extends Reftable {
}
@Override
- public long getUpdateIndex() {
- return updateIndex;
- }
-
- @Override
public void close() {
if (head != null) {
head.rc.close();
@@ -285,7 +278,7 @@ public class MergedReftable extends Reftable {
}
long updateIndex() {
- return rc.getUpdateIndex();
+ return rc.getRef().getUpdateIndex();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/RefCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/RefCursor.java
index 5d4af30a91..9749ffb906 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/RefCursor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/RefCursor.java
@@ -69,13 +69,6 @@ public abstract class RefCursor implements AutoCloseable {
public abstract Ref getRef();
/**
- * Get updateIndex that last modified the current reference.
- *
- * @return updateIndex that last modified the current reference.
- */
- public abstract long getUpdateIndex();
-
- /**
* Whether the current reference was deleted.
*
* @return {@code true} if the current reference was deleted.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java
index a1087e2023..cb02628e8d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java
@@ -280,7 +280,7 @@ public abstract class Reftable implements AutoCloseable {
if (dst == null) {
return null; // claim it doesn't exist
}
- return new SymbolicRef(ref.getName(), dst);
+ return new SymbolicRef(ref.getName(), dst, ref.getUpdateIndex());
}
/** {@inheritDoc} */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableCompactor.java
index ed73a9efbd..c4e8f69fa4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableCompactor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableCompactor.java
@@ -256,7 +256,7 @@ public class ReftableCompactor {
private void mergeRefs(MergedReftable mr) throws IOException {
try (RefCursor rc = mr.allRefs()) {
while (rc.next()) {
- writer.writeRef(rc.getRef(), rc.getUpdateIndex());
+ writer.writeRef(rc.getRef(), rc.getRef().getUpdateIndex());
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
index 81b30e4cb9..bf3a9aeca0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
@@ -479,7 +479,6 @@ public class ReftableReader extends Reftable {
private final boolean prefix;
private Ref ref;
- private long updateIndex;
BlockReader block;
RefCursorImpl(long scanEnd, byte[] match, boolean prefix) {
@@ -508,8 +507,7 @@ public class ReftableReader extends Reftable {
return false;
}
- updateIndex = minUpdateIndex + block.readUpdateIndexDelta();
- ref = block.readRef();
+ ref = block.readRef(minUpdateIndex);
if (!includeDeletes && wasDeleted()) {
continue;
}
@@ -523,11 +521,6 @@ public class ReftableReader extends Reftable {
}
@Override
- public long getUpdateIndex() {
- return updateIndex;
- }
-
- @Override
public void close() {
// Do nothing.
}
@@ -605,7 +598,6 @@ public class ReftableReader extends Reftable {
private final ObjectId match;
private Ref ref;
- private long updateIndex;
private int listIdx;
private LongList blockPos;
@@ -679,8 +671,7 @@ public class ReftableReader extends Reftable {
}
block.parseKey();
- updateIndex = minUpdateIndex + block.readUpdateIndexDelta();
- ref = block.readRef();
+ ref = block.readRef(minUpdateIndex);
ObjectId id = ref.getObjectId();
if (id != null && match.equals(id)
&& (includeDeletes || !wasDeleted())) {
@@ -695,11 +686,6 @@ public class ReftableReader extends Reftable {
}
@Override
- public long getUpdateIndex() {
- return updateIndex;
- }
-
- @Override
public void close() {
// Do nothing.
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java
index 27daaf0bb2..ddd05b3e54 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java
@@ -206,16 +206,6 @@ public class RefTreeDatabase extends RefDatabase {
/** {@inheritDoc} */
@Override
- public Ref getRef(String name) throws IOException {
- String[] needle = new String[SEARCH_PATH.length];
- for (int i = 0; i < SEARCH_PATH.length; i++) {
- needle[i] = SEARCH_PATH[i] + name;
- }
- return firstExactRef(needle);
- }
-
- /** {@inheritDoc} */
- @Override
public Ref exactRef(String name) throws IOException {
if (!repo.isBare() && name.indexOf('/') < 0 && !HEAD.equals(name)) {
// Pass through names like MERGE_HEAD, ORIG_HEAD, FETCH_HEAD.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Scanner.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Scanner.java
index 2ef0f20d8d..2fa59f3cd3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Scanner.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Scanner.java
@@ -221,10 +221,11 @@ class Scanner {
return new SymbolicRef(ref.getName(), dst);
}
- @SuppressWarnings("resource")
private static RevTree toTree(ObjectReader reader, AnyObjectId id)
throws IOException {
- return new RevWalk(reader).parseTree(id);
+ try (RevWalk rw = new RevWalk(reader)) {
+ return rw.parseTree(id);
+ }
}
private static boolean curElementHasPeelSuffix(AbstractTreeIterator itr) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java
index 7b872b1860..cd6af6a127 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java
@@ -61,7 +61,7 @@ import org.eclipse.jgit.lib.ObjectChecker;
* Validations for the git submodule fields (name, path, uri).
*
* Invalid values in these fields can cause security problems as reported in
- * CVE-2018-11235 and and CVE-2018-17456
+ * CVE-2018-11235 and CVE-2018-17456
*/
public class SubmoduleValidator {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstCommand.java
new file mode 100644
index 0000000000..0426b17f04
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstCommand.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.parser;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptySet;
+import static java.util.Collections.unmodifiableSet;
+import static java.util.stream.Collectors.toSet;
+
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * In a push, the client sends a list of commands. The first command
+ * is special, as it can include a list of capabilities at its end.
+ * <p>
+ * For example:
+ * "oid oid name\0cap1 cap cap3"
+ * <p>
+ * Not to be confused with {@link FirstWant}, nor with the first line
+ * of the reference advertisement parsed by
+ * {@code BasePackConnection.readAdvertisedRefs}.
+ * <p>
+ * This class parses the inputted command line and holds the results:
+ * the actual command line and the capabilities.
+ */
+public final class FirstCommand {
+ private final String line;
+ private final Set<String> capabilities;
+
+ /**
+ * Parse the first line of a receive-pack request.
+ *
+ * @param line
+ * line from the client.
+ * @return an instance of FirstCommand with capabilities parsed out
+ */
+ @NonNull
+ public static FirstCommand fromLine(String line) {
+ int nul = line.indexOf('\0');
+ if (nul < 0) {
+ return new FirstCommand(line, emptySet());
+ }
+ Set<String> opts =
+ asList(line.substring(nul + 1).split(" ")) //$NON-NLS-1$
+ .stream()
+ .collect(toSet());
+ return new FirstCommand(line.substring(0, nul), unmodifiableSet(opts));
+ }
+
+ private FirstCommand(String line, Set<String> capabilities) {
+ this.line = line;
+ this.capabilities = capabilities;
+ }
+
+ /** @return non-capabilities part of the line. */
+ @NonNull
+ public String getLine() {
+ return line;
+ }
+
+ /** @return capabilities parsed from the line, as an immutable set. */
+ @NonNull
+ public Set<String> getCapabilities() {
+ return capabilities;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstWant.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstWant.java
index 2dae021702..401c50776d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstWant.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstWant.java
@@ -67,7 +67,6 @@ import org.eclipse.jgit.internal.JGitText;
* This class parses the input want line and holds the results: the actual want
* line and the capabilities.
*
- * @since 5.2
*/
public class FirstWant {
private final String line;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
index c30833d0a6..6cbddec543 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
@@ -49,11 +49,15 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
+import java.text.MessageFormat;
import java.util.List;
+import org.eclipse.jgit.internal.JGitText;
+
/**
* Mutable builder to construct a commit recording the state of a project.
*
@@ -76,6 +80,8 @@ public class CommitBuilder {
private static final byte[] hcommitter = Constants.encodeASCII("committer"); //$NON-NLS-1$
+ private static final byte[] hgpgsig = Constants.encodeASCII("gpgsig"); //$NON-NLS-1$
+
private static final byte[] hencoding = Constants.encodeASCII("encoding"); //$NON-NLS-1$
private ObjectId treeId;
@@ -86,6 +92,8 @@ public class CommitBuilder {
private PersonIdent committer;
+ private GpgSignature gpgSignature;
+
private String message;
private Charset encoding;
@@ -108,7 +116,7 @@ public class CommitBuilder {
}
/**
- * Set the tree id for this commit object
+ * Set the tree id for this commit object.
*
* @param id
* the tree identity.
@@ -146,7 +154,7 @@ public class CommitBuilder {
}
/**
- * Set the committer and commit time for this object
+ * Set the committer and commit time for this object.
*
* @param newCommitter
* the committer information. Should not be null.
@@ -156,6 +164,38 @@ public class CommitBuilder {
}
/**
+ * Set the GPG signature of this commit.
+ * <p>
+ * Note, the signature set here will change the payload of the commit, i.e.
+ * the output of {@link #build()} will include the signature. Thus, the
+ * typical flow will be:
+ * <ol>
+ * <li>call {@link #build()} without a signature set to obtain payload</li>
+ * <li>create {@link GpgSignature} from payload</li>
+ * <li>set {@link GpgSignature}</li>
+ * </ol>
+ * </p>
+ *
+ * @param newSignature
+ * the signature to set or <code>null</code> to unset
+ * @since 5.3
+ */
+ public void setGpgSignature(GpgSignature newSignature) {
+ gpgSignature = newSignature;
+ }
+
+ /**
+ * Get the GPG signature of this commit.
+ *
+ * @return the GPG signature of this commit, maybe <code>null</code> if the
+ * commit is not to be signed
+ * @since 5.3
+ */
+ public GpgSignature getGpgSignature() {
+ return gpgSignature;
+ }
+
+ /**
* Get the ancestors of this commit.
*
* @return the ancestors of this commit. Never null.
@@ -250,18 +290,20 @@ public class CommitBuilder {
}
/**
- * Set the encoding for the commit information
+ * Set the encoding for the commit information.
*
* @param encodingName
* the encoding name. See
* {@link java.nio.charset.Charset#forName(String)}.
+ * @deprecated use {@link #setEncoding(Charset)} instead.
*/
+ @Deprecated
public void setEncoding(String encodingName) {
encoding = Charset.forName(encodingName);
}
/**
- * Set the encoding for the commit information
+ * Set the encoding for the commit information.
*
* @param enc
* the encoding to use.
@@ -316,6 +358,13 @@ public class CommitBuilder {
w.flush();
os.write('\n');
+ if (getGpgSignature() != null) {
+ os.write(hgpgsig);
+ os.write(' ');
+ writeGpgSignatureString(getGpgSignature().toExternalString(), os);
+ os.write('\n');
+ }
+
if (getEncoding() != UTF_8) {
os.write(hencoding);
os.write(' ');
@@ -339,6 +388,50 @@ public class CommitBuilder {
}
/**
+ * Writes signature to output as per <a href=
+ * "https://github.com/git/git/blob/master/Documentation/technical/signature-format.txt#L66,L89">gpgsig
+ * header</a>.
+ * <p>
+ * CRLF and CR will be sanitized to LF and signature will have a hanging
+ * indent of one space starting with line two.
+ * </p>
+ *
+ * @param in
+ * signature string with line breaks
+ * @param out
+ * output stream
+ * @throws IOException
+ * thrown by the output stream
+ * @throws IllegalArgumentException
+ * if the signature string contains non 7-bit ASCII chars
+ */
+ static void writeGpgSignatureString(String in, OutputStream out)
+ throws IOException, IllegalArgumentException {
+ for (int i = 0; i < in.length(); ++i) {
+ char ch = in.charAt(i);
+ if (ch == '\r') {
+ if (i + 1 < in.length() && in.charAt(i + 1) == '\n') {
+ out.write('\n');
+ out.write(' ');
+ ++i;
+ } else {
+ out.write('\n');
+ out.write(' ');
+ }
+ } else if (ch == '\n') {
+ out.write('\n');
+ out.write(' ');
+ } else {
+ // sanity check
+ if (ch > 127)
+ throw new IllegalArgumentException(MessageFormat
+ .format(JGitText.get().notASCIIString, in));
+ out.write(ch);
+ }
+ }
+ }
+
+ /**
* Format this builder's state as a commit object.
*
* @return this object in the canonical commit format, suitable for storage
@@ -377,6 +470,10 @@ public class CommitBuilder {
r.append(committer != null ? committer.toString() : "NOT_SET");
r.append("\n");
+ r.append("gpgSignature ");
+ r.append(gpgSignature != null ? gpgSignature.toString() : "NOT_SET");
+ r.append("\n");
+
if (encoding != null && encoding != UTF_8) {
r.append("encoding ");
r.append(encoding.name());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignature.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignature.java
new file mode 100644
index 0000000000..663f850f0a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignature.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018, Salesforce.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lib;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
+import java.io.Serializable;
+
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * A structure for holding GPG signature together with additional related data.
+ *
+ * @since 5.3
+ */
+public class GpgSignature implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private byte[] signature;
+
+ /**
+ * Creates a new instance with the specified signature
+ *
+ * @param signature
+ * the signature
+ */
+ public GpgSignature(@NonNull byte[] signature) {
+ this.signature = signature;
+ }
+
+ /**
+ * Format for Git storage.
+ * <p>
+ * This returns the ASCII Armor as per
+ * https://tools.ietf.org/html/rfc4880#section-6.2.
+ * </p>
+ *
+ * @return a string of the signature ready to be embedded in a Git object
+ */
+ public String toExternalString() {
+ return new String(signature, US_ASCII);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @SuppressWarnings("nls")
+ public String toString() {
+ final StringBuilder r = new StringBuilder();
+
+ r.append("GpgSignature[");
+ r.append(
+ this.signature != null ? "length " + signature.length : "null");
+ r.append("]");
+
+ return r.toString();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java
new file mode 100644
index 0000000000..99a23c6e42
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018, Salesforce.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lib;
+
+import 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;
+
+/**
+ * Creates GPG signatures for Git objects.
+ *
+ * @since 5.3
+ */
+public abstract class GpgSigner {
+
+ private static GpgSigner defaultSigner = new BouncyCastleGpgSigner();
+
+ /**
+ * Get the default signer, or <code>null</code>.
+ *
+ * @return the default signer, or <code>null</code>.
+ */
+ public static GpgSigner getDefault() {
+ return defaultSigner;
+ }
+
+ /**
+ * Set the default signer.
+ *
+ * @param signer
+ * the new default signer, may be <code>null</code> to select no
+ * default.
+ */
+ public static void setDefault(GpgSigner signer) {
+ GpgSigner.defaultSigner = signer;
+ }
+
+ /**
+ * Signs the specified commit.
+ *
+ * <p>
+ * Implementors should obtain the payload for signing from the specified
+ * commit via {@link CommitBuilder#build()} and create a proper
+ * {@link GpgSignature}. The generated signature must be set on the
+ * specified {@code commit} (see
+ * {@link CommitBuilder#setGpgSignature(GpgSignature)}).
+ * </p>
+ * <p>
+ * Any existing signature on the commit must be discarded prior obtaining
+ * the payload via {@link CommitBuilder#build()}.
+ * </p>
+ *
+ * @param commit
+ * the commit to sign (must not be <code>null</code> and must be
+ * complete to allow proper calculation of payload)
+ * @param gpgSigningKey
+ * the signing key to locate (passed as is to the GPG signing
+ * tool as is; eg., value of <code>user.signingkey</code>)
+ * @param committer
+ * the signing identity (to help with key lookup in case signing
+ * key is not specified)
+ * @param credentialsProvider
+ * provider to use when querying for signing key credentials (eg.
+ * passphrase)
+ * @throws CanceledException
+ * when signing was canceled (eg., user aborted when entering
+ * passphrase)
+ */
+ public abstract void sign(@NonNull CommitBuilder commit,
+ @Nullable String gpgSigningKey, @NonNull PersonIdent committer,
+ CredentialsProvider credentialsProvider) throws CanceledException;
+
+ /**
+ * Indicates if a signing key is available for the specified committer
+ * and/or signing key.
+ *
+ * @param gpgSigningKey
+ * the signing key to locate (passed as is to the GPG signing
+ * tool as is; eg., value of <code>user.signingkey</code>)
+ * @param committer
+ * the signing identity (to help with key lookup in case signing
+ * key is not specified)
+ * @param credentialsProvider
+ * provider to use when querying for signing key credentials (eg.
+ * passphrase)
+ * @return <code>true</code> if a signing key is available,
+ * <code>false</code> otherwise
+ * @throws CanceledException
+ * when signing was canceled (eg., user aborted when entering
+ * passphrase)
+ */
+ public abstract boolean canLocateSigningKey(@Nullable String gpgSigningKey,
+ @NonNull PersonIdent committer,
+ CredentialsProvider credentialsProvider) throws CanceledException;
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
index 764f890158..0e96138c00 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
@@ -49,6 +49,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.InvalidObjectIdException;
import org.eclipse.jgit.util.NB;
import org.eclipse.jgit.util.RawParseUtils;
@@ -86,7 +87,10 @@ public class ObjectId extends AnyObjectId implements Serializable {
* the string to test.
* @return true if the string can converted into an ObjectId.
*/
- public static final boolean isId(String id) {
+ public static final boolean isId(@Nullable String id) {
+ if (id == null) {
+ return false;
+ }
if (id.length() != Constants.OBJECT_ID_STRING_LENGTH)
return false;
try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
index 22aaa3ad73..b791c64552 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
@@ -67,7 +67,26 @@ public abstract class ObjectIdRef implements Ref {
*/
public Unpeeled(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id) {
- super(st, name, id);
+ super(st, name, id, -1);
+ }
+
+ /**
+ * Create a new ref pairing with update index.
+ *
+ * @param st
+ * method used to store this ref.
+ * @param name
+ * name of this ref.
+ * @param id
+ * current value of the ref. May be {@code null} to indicate
+ * a ref that does not exist yet.
+ * @param updateIndex
+ * number increasing with each update to the reference.
+ * @since 5.3
+ */
+ public Unpeeled(@NonNull Storage st, @NonNull String name,
+ @Nullable ObjectId id, long updateIndex) {
+ super(st, name, id, updateIndex);
}
@Override
@@ -100,7 +119,29 @@ public abstract class ObjectIdRef implements Ref {
*/
public PeeledTag(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id, @NonNull ObjectId p) {
- super(st, name, id);
+ super(st, name, id, -1);
+ peeledObjectId = p;
+ }
+
+ /**
+ * Create a new ref pairing with update index.
+ *
+ * @param st
+ * method used to store this ref.
+ * @param name
+ * name of this ref.
+ * @param id
+ * current value of the ref. May be {@code null} to indicate
+ * a ref that does not exist yet.
+ * @param p
+ * the first non-tag object that tag {@code id} points to.
+ * @param updateIndex
+ * number increasing with each update to the reference.
+ * @since 5.3
+ */
+ public PeeledTag(@NonNull Storage st, @NonNull String name,
+ @Nullable ObjectId id, @NonNull ObjectId p, long updateIndex) {
+ super(st, name, id, updateIndex);
peeledObjectId = p;
}
@@ -131,7 +172,26 @@ public abstract class ObjectIdRef implements Ref {
*/
public PeeledNonTag(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id) {
- super(st, name, id);
+ super(st, name, id, -1);
+ }
+
+ /**
+ * Create a new ref pairing with update index.
+ *
+ * @param st
+ * method used to store this ref.
+ * @param name
+ * name of this ref.
+ * @param id
+ * current value of the ref. May be {@code null} to indicate
+ * a ref that does not exist yet.
+ * @param updateIndex
+ * number increasing with each update to the reference.
+ * @since 5.3
+ */
+ public PeeledNonTag(@NonNull Storage st, @NonNull String name,
+ @Nullable ObjectId id, long updateIndex) {
+ super(st, name, id, updateIndex);
}
@Override
@@ -152,6 +212,8 @@ public abstract class ObjectIdRef implements Ref {
private final ObjectId objectId;
+ private final long updateIndex;
+
/**
* Create a new ref pairing.
*
@@ -162,12 +224,17 @@ public abstract class ObjectIdRef implements Ref {
* @param id
* current value of the ref. May be {@code null} to indicate a
* ref that does not exist yet.
+ * @param updateIndex
+ * number that increases with each ref update. Set to -1 if the
+ * storage doesn't support versioning.
+ * @since 5.3
*/
protected ObjectIdRef(@NonNull Storage st, @NonNull String name,
- @Nullable ObjectId id) {
+ @Nullable ObjectId id, long updateIndex) {
this.name = name;
this.storage = st;
this.objectId = id;
+ this.updateIndex = updateIndex;
}
/** {@inheritDoc} */
@@ -211,6 +278,18 @@ public abstract class ObjectIdRef implements Ref {
return storage;
}
+ /**
+ * {@inheritDoc}
+ * @since 5.3
+ */
+ @Override
+ public long getUpdateIndex() {
+ if (updateIndex == -1) {
+ throw new UnsupportedOperationException();
+ }
+ return updateIndex;
+ }
+
/** {@inheritDoc} */
@NonNull
@Override
@@ -220,7 +299,9 @@ public abstract class ObjectIdRef implements Ref {
r.append(getName());
r.append('=');
r.append(ObjectId.toString(getObjectId()));
- r.append(']');
+ r.append('(');
+ r.append(updateIndex); // Print value, even if -1
+ r.append(")]"); //$NON-NLS-1$
return r.toString();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
index faabbf892f..32c8b06c91 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
@@ -217,4 +217,27 @@ public interface Ref {
*/
@NonNull
Storage getStorage();
+
+ /**
+ * Indicator of the relative order between updates of a specific reference
+ * name. A number that increases when a reference is updated.
+ * <p>
+ * With symbolic references, the update index refers to updates of the
+ * symbolic reference itself. For example, if HEAD points to
+ * refs/heads/master, then the update index for exactRef("HEAD") will only
+ * increase when HEAD changes to point to another ref, regardless of how
+ * many times refs/heads/master is updated.
+ * <p>
+ * Should not be used unless the {@code RefDatabase} that instantiated the
+ * ref supports versioning (see {@link RefDatabase#hasVersioning()})
+ *
+ * @return the update index (i.e. version) of this reference.
+ * @throws UnsupportedOperationException
+ * if the creator of the instance (e.g. {@link RefDatabase})
+ * doesn't support versioning and doesn't override this method
+ * @since 5.3
+ */
+ default long getUpdateIndex() {
+ throw new UnsupportedOperationException();
+ }
}
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 68929b4220..877792097c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -69,10 +69,10 @@ public abstract class RefDatabase {
/**
* Order of prefixes to search when using non-absolute references.
* <p>
- * The implementation's {@link #getRef(String)} method must take this search
- * space into consideration when locating a reference by name. The first
- * entry in the path is always {@code ""}, ensuring that absolute references
- * are resolved without further mangling.
+ * {@link #findRef(String)} takes this search space into consideration
+ * when locating a reference by name. The first entry in the path is
+ * always {@code ""}, ensuring that absolute references are resolved
+ * without further mangling.
*/
protected static final String[] SEARCH_PATH = { "", //$NON-NLS-1$
Constants.R_REFS, //
@@ -111,6 +111,19 @@ public abstract class RefDatabase {
public abstract void close();
/**
+ * With versioning, each reference has a version number that increases on
+ * update. See {@link Ref#getUpdateIndex()}.
+ *
+ * @implSpec This method returns false by default. Implementations
+ * supporting versioning must override it to return true.
+ * @return true if the implementation assigns update indices to references.
+ * @since 5.3
+ */
+ public boolean hasVersioning() {
+ return false;
+ }
+
+ /**
* Determine if a proposed reference name overlaps with an existing one.
* <p>
* Reference names use '/' as a component separator, and may be stored in a
@@ -244,6 +257,23 @@ public abstract class RefDatabase {
}
/**
+ * Compatibility synonym for {@link #findRef(String)}.
+ *
+ * @param name
+ * the name of the reference. May be a short name which must be
+ * searched for using the standard {@link #SEARCH_PATH}.
+ * @return the reference (if it exists); else {@code null}.
+ * @throws IOException
+ * the reference space cannot be accessed.
+ * @deprecated Use {@link #findRef(String)} instead.
+ */
+ @Deprecated
+ @Nullable
+ public final Ref getRef(String name) throws IOException {
+ return findRef(name);
+ }
+
+ /**
* Read a single reference.
* <p>
* Aside from taking advantage of {@link #SEARCH_PATH}, this method may be
@@ -259,14 +289,21 @@ public abstract class RefDatabase {
* @return the reference (if it exists); else {@code null}.
* @throws java.io.IOException
* the reference space cannot be accessed.
+ * @since 5.3
*/
@Nullable
- public abstract Ref getRef(String name) throws IOException;
+ public final Ref findRef(String name) throws IOException {
+ String[] names = new String[SEARCH_PATH.length];
+ for (int i = 0; i < SEARCH_PATH.length; i++) {
+ names[i] = SEARCH_PATH[i] + name;
+ }
+ return firstExactRef(names);
+ }
/**
* Read a single reference.
* <p>
- * Unlike {@link #getRef}, this method expects an unshortened reference
+ * Unlike {@link #findRef}, this method expects an unshortened reference
* name and does not search using the standard {@link #SEARCH_PATH}.
*
* @param name
@@ -277,13 +314,7 @@ public abstract class RefDatabase {
* @since 4.1
*/
@Nullable
- public Ref exactRef(String name) throws IOException {
- Ref ref = getRef(name);
- if (ref == null || !name.equals(ref.getName())) {
- return null;
- }
- return ref;
- }
+ public abstract Ref exactRef(String name) throws IOException;
/**
* Read the specified references.
@@ -462,7 +493,7 @@ public abstract class RefDatabase {
* <p>
* The result list includes non-ref items such as MERGE_HEAD and
* FETCH_RESULT cast to be refs. The names of these refs are not returned by
- * <code>getRefs()</code> but are accepted by {@link #getRef(String)}
+ * <code>getRefs()</code> but are accepted by {@link #findRef(String)}
* and {@link #exactRef(String)}.
*
* @return a list of additional refs
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java
index a05daa00ab..0bd34b51ac 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java
@@ -184,7 +184,7 @@ public abstract class RefRename {
* the current value of {@code HEAD} cannot be read.
*/
protected boolean needToUpdateHEAD() throws IOException {
- Ref head = source.getRefDatabase().getRef(Constants.HEAD);
+ Ref head = source.getRefDatabase().exactRef(Constants.HEAD);
if (head != null && head.isSymbolic()) {
head = head.getTarget();
return head.getName().equals(source.getName());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
index fc3ea8467a..1ce1528344 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
@@ -665,7 +665,7 @@ public abstract class RefUpdate {
: getRef().getLeaf().getName();
if (myName.startsWith(Constants.R_HEADS) && !getRepository().isBare()) {
// Don't allow the currently checked out branch to be deleted.
- Ref head = getRefDatabase().getRef(Constants.HEAD);
+ Ref head = getRefDatabase().exactRef(Constants.HEAD);
while (head != null && head.isSymbolic()) {
head = head.getTarget();
if (myName.equals(head.getName()))
@@ -708,7 +708,7 @@ public abstract class RefUpdate {
if (!tryLock(false))
return Result.LOCK_FAILURE;
- final Ref old = getRefDatabase().getRef(getName());
+ final Ref old = getRefDatabase().exactRef(getName());
if (old != null && old.isSymbolic()) {
final Ref dst = old.getTarget();
if (target.equals(dst.getName()))
@@ -718,7 +718,7 @@ public abstract class RefUpdate {
if (old != null && old.getObjectId() != null)
setOldObjectId(old.getObjectId());
- final Ref dst = getRefDatabase().getRef(target);
+ final Ref dst = getRefDatabase().exactRef(target);
if (dst != null && dst.getObjectId() != null)
setNewObjectId(dst.getObjectId());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index 77d268a3bd..a61897a652 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -57,6 +57,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.UncheckedIOException;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.Collection;
@@ -297,7 +298,7 @@ public abstract class Repository implements AutoCloseable {
/**
* Get the used file system abstraction.
*
- * @return the used file system abstraction, or or {@code null} if
+ * @return the used file system abstraction, or {@code null} if
* repository isn't local.
*/
/*
@@ -319,13 +320,14 @@ public abstract class Repository implements AutoCloseable {
* a {@link org.eclipse.jgit.lib.AnyObjectId} object.
* @return true if the specified object is stored in this repo or any of the
* known shared repositories.
+ * @deprecated use {@code getObjectDatabase().has(objectId)}
*/
+ @Deprecated
public boolean hasObject(AnyObjectId objectId) {
try {
return getObjectDatabase().has(objectId);
} catch (IOException e) {
- // Legacy API, assume error means "no"
- return false;
+ throw new UncheckedIOException(e);
}
}
@@ -849,7 +851,7 @@ public abstract class Repository implements AutoCloseable {
return ObjectId.fromString(revstr);
if (Repository.isValidRefName("x/" + revstr)) { //$NON-NLS-1$
- Ref r = getRefDatabase().getRef(revstr);
+ Ref r = getRefDatabase().findRef(revstr);
if (r != null)
return r.getObjectId();
}
@@ -1079,7 +1081,7 @@ public abstract class Repository implements AutoCloseable {
*
* @param name
* the name of the ref to lookup. May be a short-hand form, e.g.
- * "master" which is is automatically expanded to
+ * "master" which is automatically expanded to
* "refs/heads/master" if "refs/heads/master" already exists.
* @return the Ref with the given name, or {@code null} if it does not exist
* @throws java.io.IOException
@@ -1087,7 +1089,7 @@ public abstract class Repository implements AutoCloseable {
*/
@Nullable
public final Ref findRef(String name) throws IOException {
- return getRefDatabase().getRef(name);
+ return getRefDatabase().findRef(name);
}
/**
@@ -1103,7 +1105,7 @@ public abstract class Repository implements AutoCloseable {
try {
return getRefDatabase().getRefs(RefDatabase.ALL);
} catch (IOException e) {
- return new HashMap<>();
+ throw new UncheckedIOException(e);
}
}
@@ -1121,7 +1123,7 @@ public abstract class Repository implements AutoCloseable {
try {
return getRefDatabase().getRefs(Constants.R_TAGS);
} catch (IOException e) {
- return new HashMap<>();
+ throw new UncheckedIOException(e);
}
}
@@ -1320,9 +1322,7 @@ public abstract class Repository implements AutoCloseable {
return RepositoryState.MERGING_RESOLVED;
}
} catch (IOException e) {
- // Can't decide whether unmerged paths exists. Return
- // MERGING state to be on the safe side (in state MERGING
- // you are not allow to do anything)
+ throw new UncheckedIOException(e);
}
return RepositoryState.MERGING;
}
@@ -1337,7 +1337,7 @@ public abstract class Repository implements AutoCloseable {
return RepositoryState.CHERRY_PICKING_RESOLVED;
}
} catch (IOException e) {
- // fall through to CHERRY_PICKING
+ throw new UncheckedIOException(e);
}
return RepositoryState.CHERRY_PICKING;
@@ -1350,7 +1350,7 @@ public abstract class Repository implements AutoCloseable {
return RepositoryState.REVERTING_RESOLVED;
}
} catch (IOException e) {
- // fall through to REVERTING
+ throw new UncheckedIOException(e);
}
return RepositoryState.REVERTING;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java
index d4b83b0128..00fcf52037 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java
@@ -58,6 +58,8 @@ public class SymbolicRef implements Ref {
private final Ref target;
+ private final long updateIndex;
+
/**
* Create a new ref pairing.
*
@@ -69,6 +71,25 @@ public class SymbolicRef implements Ref {
public SymbolicRef(@NonNull String refName, @NonNull Ref target) {
this.name = refName;
this.target = target;
+ this.updateIndex = -1;
+ }
+
+ /**
+ * Create a new ref pairing.
+ *
+ * @param refName
+ * name of this ref.
+ * @param target
+ * the ref we reference and derive our value from.
+ * @param updateIndex
+ * index that increases with each update of the reference
+ * @since 5.3
+ */
+ public SymbolicRef(@NonNull String refName, @NonNull Ref target,
+ long updateIndex) {
+ this.name = refName;
+ this.target = target;
+ this.updateIndex = updateIndex;
}
/** {@inheritDoc} */
@@ -128,6 +149,18 @@ public class SymbolicRef implements Ref {
return getLeaf().isPeeled();
}
+ /**
+ * {@inheritDoc}
+ * @since 5.3
+ */
+ @Override
+ public long getUpdateIndex() {
+ if (updateIndex == -1) {
+ throw new UnsupportedOperationException();
+ }
+ return updateIndex;
+ }
+
/** {@inheritDoc} */
@SuppressWarnings("nls")
@Override
@@ -143,7 +176,9 @@ public class SymbolicRef implements Ref {
r.append(cur.getName());
r.append('=');
r.append(ObjectId.toString(cur.getObjectId()));
- r.append("]");
+ r.append("(");
+ r.append(updateIndex); // Print value, even if -1
+ r.append(")]");
return r.toString();
}
}
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
new file mode 100644
index 0000000000..ef9d7ee393
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKey.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018, Salesforce.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lib.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
new file mode 100644
index 0000000000..091667db01
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2018, Salesforce.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lib.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.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+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.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPSecretKey;
+import org.bouncycastle.openpgp.PGPSecretKeyRing;
+import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
+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.SystemReader;
+
+/**
+ * Locates GPG keys from either <code>~/.gnupg/private-keys-v1.d</code> or
+ * <code>~/.gnupg/secring.gpg</code>
+ */
+class BouncyCastleGpgKeyLocator {
+
+ 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_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) throws IOException {
+ try (InputStream in = newInputStream(keyFile)) {
+ return new SExprParser(calculatorProvider).parseSecretKey(
+ new BufferedInputStream(in), passphraseProvider, publicKey);
+ } catch (PGPException | ClassCastException e) {
+ return null;
+ }
+ }
+
+ private boolean containsSigningKey(String userId) {
+ return userId.toLowerCase(Locale.ROOT)
+ .contains(signingKey.toLowerCase(Locale.ROOT));
+ }
+
+ private PGPPublicKey findPublicKeyByKeyId(KeyBlob keyBlob)
+ throws IOException {
+ for (KeyInformation keyInfo : keyBlob.getKeyInformation()) {
+ if (signingKey.toLowerCase(Locale.ROOT)
+ .equals(Hex.toHexString(keyInfo.getKeyID())
+ .toLowerCase(Locale.ROOT))) {
+ return getFirstPublicKey(keyBlob);
+ }
+ }
+ return null;
+ }
+
+ private PGPPublicKey findPublicKeyByUserId(KeyBlob keyBlob)
+ throws IOException {
+ for (UserID userID : keyBlob.getUserIds()) {
+ if (containsSigningKey(userID.getUserIDAsString())) {
+ return getFirstPublicKey(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
+ */
+ private PGPPublicKey findPublicKeyInKeyBox(Path keyboxFile)
+ throws IOException {
+ KeyBox keyBox = readKeyBoxFile(keyboxFile);
+ for (KeyBlob keyBlob : keyBox.getKeyBlobs()) {
+ if (keyBlob.getType() == BlobType.OPEN_PGP_BLOB) {
+ PGPPublicKey key = findPublicKeyByKeyId(keyBlob);
+ if (key != null) {
+ return key;
+ }
+ key = findPublicKeyByUserId(keyBlob);
+ if (key != null) {
+ return key;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Use pubring.kbx when available, if not fallback to secring.gpg or secret
+ * key path provided to parse and return secret key
+ *
+ * @return the secret key
+ * @throws IOException
+ * in case of issues reading key files
+ * @throws PGPException
+ * in case of issues finding a key
+ * @throws CanceledException
+ * @throws URISyntaxException
+ * @throws UnsupportedCredentialItem
+ */
+ public BouncyCastleGpgKey findSecretKey()
+ throws IOException, PGPException, CanceledException,
+ UnsupportedCredentialItem, URISyntaxException {
+ if (exists(USER_KEYBOX_PATH)) {
+ PGPPublicKey publicKey = //
+ findPublicKeyInKeyBox(USER_KEYBOX_PATH);
+
+ if (publicKey != null) {
+ return findSecretKeyForKeyBoxPublicKey(publicKey,
+ USER_KEYBOX_PATH);
+ }
+
+ throw new PGPException(MessageFormat
+ .format(JGitText.get().gpgNoPublicKeyFound, signingKey));
+ } else if (exists(USER_PGP_LEGACY_SECRING_FILE)) {
+ PGPSecretKey secretKey = findSecretKeyInLegacySecring(signingKey,
+ USER_PGP_LEGACY_SECRING_FILE);
+
+ if (secretKey != null) {
+ return new BouncyCastleGpgKey(secretKey, USER_PGP_LEGACY_SECRING_FILE);
+ }
+
+ throw new PGPException(MessageFormat.format(
+ JGitText.get().gpgNoKeyInLegacySecring, signingKey));
+ }
+
+ throw new PGPException(JGitText.get().gpgNoKeyring);
+ }
+
+ 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) {
+ return new BouncyCastleGpgKey(secretKey, userKeyboxPath);
+ }
+ }
+
+ passphrasePrompt.clear();
+ throw new PGPException(MessageFormat.format(
+ JGitText.get().gpgNoSecretKeyForPublicKey,
+ Long.toHexString(publicKey.getKeyID())));
+ } 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());
+
+ 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(signingkey.toLowerCase(Locale.ROOT))) {
+ return key;
+ }
+ // try user id
+ Iterator<String> userIDs = key.getUserIDs();
+ while (userIDs.hasNext()) {
+ String userId = userIDs.next();
+ if (containsSigningKey(userId)) {
+ return key;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private PGPPublicKey getFirstPublicKey(KeyBlob keyBlob) throws IOException {
+ return ((PublicKeyRingBlob) keyBlob).getPGPPublicKeyRing()
+ .getPublicKey();
+ }
+
+ private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException {
+ KeyBox keyBox;
+ try (InputStream in = new BufferedInputStream(
+ newInputStream(keyboxFile))) {
+ // note: KeyBox constructor reads in the whole InputStream at once
+ // this code will change in 1.61 to
+ // either 'new BcKeyBox(in)' or 'new JcaKeyBoxBuilder().build(in)'
+ keyBox = new KeyBox(in, new JcaKeyFingerprintCalculator());
+ }
+ 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
new file mode 100644
index 0000000000..2efe962918
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyPassphrasePrompt.java
@@ -0,0 +1,134 @@
+/*-
+ * Copyright (C) 2019, Salesforce.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lib.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
new file mode 100644
index 0000000000..4d696dd9e7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2018, Salesforce.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lib.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URISyntaxException;
+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 | URISyntaxException e) {
+ return false;
+ }
+ }
+
+ private BouncyCastleGpgKey locateSigningKey(@Nullable String gpgSigningKey,
+ PersonIdent committer,
+ BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt)
+ throws CanceledException, UnsupportedCredentialItem, IOException,
+ 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 | URISyntaxException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java
index 062d86f8a2..a533bf5653 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java
@@ -119,7 +119,7 @@ public class MergeResult<S extends Sequence> implements Iterable<MergeChunk> {
/**
* Returns the common predecessor sequence and the merged sequence in one
- * list. The common predecessor is is the first element in the list
+ * list. The common predecessor is the first element in the list
*
* @return the common predecessor at position 0 followed by the merged
* sequences.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index 909f3b15d8..75334ddb0c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -1027,13 +1027,16 @@ public class ResolveMerger extends ThreeWayMerger {
throws IOException {
TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(
db != null ? nonNullRepo().getDirectory() : null, inCoreLimit);
+ boolean success = false;
try {
new MergeFormatter().formatMerge(buf, result,
Arrays.asList(commitNames), UTF_8);
buf.close();
- } catch (IOException e) {
- buf.destroy();
- throw e;
+ success = true;
+ } finally {
+ if (!success) {
+ buf.destroy();
+ }
}
return buf;
}
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 89a87af9eb..375cd32041 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
@@ -79,7 +79,7 @@ public class NLS {
/**
* Sets the locale for the calling thread.
* <p>
- * The {@link #getBundleFor(Class)} method will honor this setting if if it
+ * The {@link #getBundleFor(Class)} method will honor this setting if it
* is supported by the provided resource bundle property files. Otherwise,
* it will use a fall back locale as described in the
* {@link TranslationBundle}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java
index 5e153164ad..45508ce027 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java
@@ -141,7 +141,8 @@ public class PlotCommitList<L extends PlotLane> extends
final PlotCommit<L> c = currCommit.children[0];
currCommit.lane = c.lane;
Integer len = laneLength.get(currCommit.lane);
- len = Integer.valueOf(len.intValue() + 1);
+ len = len != null ? Integer.valueOf(len.intValue() + 1)
+ : Integer.valueOf(0);
laneLength.put(currCommit.lane, len);
} else {
// More than one child, or our child is a merge.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
index e5903c9117..fd578da333 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
@@ -330,11 +330,11 @@ public class ObjectWalk extends RevWalk {
*
* @return next most recent object; null if traversal is over.
* @throws org.eclipse.jgit.errors.MissingObjectException
- * one or or more of the next objects are not available from the
+ * one or more of the next objects are not available from the
* object database, but were thought to be candidates for
* traversal. This usually indicates a broken link.
* @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
- * one or or more of the objects in a tree do not match the type
+ * one or more of the objects in a tree do not match the type
* indicated.
* @throws java.io.IOException
* a pack file or loose object could not be read.
@@ -534,11 +534,11 @@ public class ObjectWalk extends RevWalk {
* provides some detail about the connectivity failure.
*
* @throws org.eclipse.jgit.errors.MissingObjectException
- * one or or more of the next objects are not available from the
+ * one or more of the next objects are not available from the
* object database, but were thought to be candidates for
* traversal. This usually indicates a broken link.
* @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
- * one or or more of the objects in a tree do not match the type
+ * one or more of the objects in a tree do not match the type
* indicated.
* @throws java.io.IOException
* a pack file or loose object could not be read.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index 400ea33c21..0a43e8fb1a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -394,11 +394,11 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
* <code>base</code> (and thus <code>base</code> is fully merged
* into <code>tip</code>); false otherwise.
* @throws org.eclipse.jgit.errors.MissingObjectException
- * one or or more of the next commit's parents are not available
+ * one or more of the next commit's parents are not available
* from the object database, but were thought to be candidates
* for traversal. This usually indicates a broken link.
* @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
- * one or or more of the next commit's parents are not actually
+ * one or more of the next commit's parents are not actually
* commit objects.
* @throws java.io.IOException
* a pack file or loose object could not be read.
@@ -431,11 +431,11 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
*
* @return next most recent commit; null if traversal is over.
* @throws org.eclipse.jgit.errors.MissingObjectException
- * one or or more of the next commit's parents are not available
+ * one or more of the next commit's parents are not available
* from the object database, but were thought to be candidates
* for traversal. This usually indicates a broken link.
* @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
- * one or or more of the next commit's parents are not actually
+ * one or more of the next commit's parents are not actually
* commit objects.
* @throws java.io.IOException
* a pack file or loose object could not be read.
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 c2e6a42001..ff499764c9 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
@@ -251,7 +251,7 @@ public class WindowCacheConfig {
* Install this configuration as the live settings.
* <p>
* The new configuration is applied immediately. If the new limits are
- * smaller than what what is currently cached, older entries will be purged
+ * smaller than what is currently cached, older entries will be purged
* as soon as possible to allow the cache to meet the new limit.
*
* @since 3.0
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
index 6bd32dd873..6722e9bdcd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
@@ -1100,7 +1100,7 @@ public class PackConfig {
}
/**
- * Get the the age in days that marks a branch as "inactive".
+ * Get the age in days that marks a branch as "inactive".
*
* Default setting: {@value #DEFAULT_BITMAP_INACTIVE_BRANCH_AGE_IN_DAYS}
*
@@ -1112,7 +1112,7 @@ public class PackConfig {
}
/**
- * Set the the age in days that marks a branch as "inactive".
+ * Set the age in days that marks a branch as "inactive".
*
* Default setting: {@value #DEFAULT_BITMAP_INACTIVE_BRANCH_AGE_IN_DAYS}
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java
index 72b4255df9..8512f2d9fe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java
@@ -53,7 +53,7 @@ public interface AdvertiseRefsHook {
* <p>
* The method implementations do nothing to preserve the default behavior; see
* {@link UploadPack#setAdvertisedRefs(java.util.Map)} and
- * {@link BaseReceivePack#setAdvertisedRefs(java.util.Map,java.util.Set)}.
+ * {@link ReceivePack#setAdvertisedRefs(java.util.Map,java.util.Set)}.
*/
AdvertiseRefsHook DEFAULT = new AdvertiseRefsHook() {
@Override
@@ -85,7 +85,7 @@ public interface AdvertiseRefsHook {
*
* @param receivePack
* instance on which to call
- * {@link org.eclipse.jgit.transport.BaseReceivePack#setAdvertisedRefs(java.util.Map,java.util.Set)}
+ * {@link org.eclipse.jgit.transport.ReceivePack#setAdvertisedRefs(java.util.Map,java.util.Set)}
* if necessary.
* @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
* abort; the message will be sent to the user.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java
index 4ef3e1a974..12238a1f77 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java
@@ -52,7 +52,7 @@ import java.util.List;
* Hooks are run in the order passed to the constructor. A hook may inspect or
* modify the results of the previous hooks in the chain by calling
* {@link org.eclipse.jgit.transport.UploadPack#getAdvertisedRefs()}, or
- * {@link org.eclipse.jgit.transport.BaseReceivePack#getAdvertisedRefs()} or
+ * {@link org.eclipse.jgit.transport.ReceivePack#getAdvertisedRefs()} or
* {@link org.eclipse.jgit.transport.BaseReceivePack#getAdvertisedObjects()}.
*/
public class AdvertiseRefsHookChain implements AdvertiseRefsHook {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
index 69624ff7a3..847e901980 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
@@ -338,7 +338,7 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
for (Ref r : getRefs()) {
// only add objects that we actually have
ObjectId oid = r.getObjectId();
- if (local.hasObject(oid))
+ if (local.getObjectDatabase().has(oid))
remoteObjects.add(oid);
}
remoteObjects.addAll(additionalHaves);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
index 03763368a8..6f17620d91 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
@@ -80,6 +80,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.parser.FirstCommand;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.Config;
@@ -119,10 +120,14 @@ import org.eclipse.jgit.util.io.TimeoutOutputStream;
* Subclasses compose these operations into full service implementations.
*/
public abstract class BaseReceivePack {
- /** Data in the first line of a request, the line itself plus capabilities. */
+ /**
+ * Data in the first line of a request, the line itself plus capabilities.
+ *
+ * @deprecated Use {@link FirstCommand} instead.
+ */
+ @Deprecated
public static class FirstLine {
- private final String line;
- private final Set<String> capabilities;
+ private final FirstCommand command;
/**
* Parse the first line of a receive-pack request.
@@ -131,33 +136,25 @@ public abstract class BaseReceivePack {
* line from the client.
*/
public FirstLine(String line) {
- final HashSet<String> caps = new HashSet<>();
- final int nul = line.indexOf('\0');
- if (nul >= 0) {
- for (String c : line.substring(nul + 1).split(" ")) //$NON-NLS-1$
- caps.add(c);
- this.line = line.substring(0, nul);
- } else
- this.line = line;
- this.capabilities = Collections.unmodifiableSet(caps);
+ command = FirstCommand.fromLine(line);
}
/** @return non-capabilities part of the line. */
public String getLine() {
- return line;
+ return command.getLine();
}
/** @return capabilities parsed from the line. */
public Set<String> getCapabilities() {
- return capabilities;
+ return command.getCapabilities();
}
}
/** Database we write the stored objects into. */
- private final Repository db;
+ final Repository db;
/** Revision traversal support over {@link #db}. */
- private final RevWalk walk;
+ final RevWalk walk;
/**
* Is the client connection a bi-directional socket or pipe?
@@ -207,7 +204,7 @@ public abstract class BaseReceivePack {
private AdvertiseRefsHook advertiseRefsHook;
/** Filter used while advertising the refs to the client. */
- private RefFilter refFilter;
+ RefFilter refFilter;
/** Timeout in seconds to wait for client interaction. */
private int timeout;
@@ -242,10 +239,10 @@ public abstract class BaseReceivePack {
private PackParser parser;
/** The refs we advertised as existing at the start of the connection. */
- private Map<String, Ref> refs;
+ Map<String, Ref> refs;
/** All SHA-1s shown to the client, which can be possible edges. */
- private Set<ObjectId> advertisedHaves;
+ Set<ObjectId> advertisedHaves;
/** Capabilities requested by the client. */
private Set<String> enabledCapabilities;
@@ -278,7 +275,7 @@ public abstract class BaseReceivePack {
private PushCertificateParser pushCertificateParser;
private SignedPushConfig signedPushConfig;
- private PushCertificate pushCert;
+ PushCertificate pushCert;
private ReceivedPackStatistics stats;
/**
@@ -289,10 +286,10 @@ public abstract class BaseReceivePack {
* @return the parsed certificate, or null if push certificates are disabled
* or no cert was presented by the client.
* @since 4.1
+ * @deprecated use {@link ReceivePack#getPushCertificate}.
*/
- public PushCertificate getPushCertificate() {
- return pushCert;
- }
+ @Deprecated
+ public abstract PushCertificate getPushCertificate();
/**
* Set the push certificate used to verify the pusher's identity.
@@ -303,10 +300,10 @@ public abstract class BaseReceivePack {
* @param cert
* the push certificate to set.
* @since 4.1
+ * @deprecated use {@link ReceivePack#setPushCertificate(PushCertificate)}.
*/
- public void setPushCertificate(PushCertificate cert) {
- pushCert = cert;
- }
+ @Deprecated
+ public abstract void setPushCertificate(PushCertificate cert);
/**
* Create a new pack receive for an open repository.
@@ -424,29 +421,29 @@ public abstract class BaseReceivePack {
* Get the repository this receive completes into.
*
* @return the repository this receive completes into.
+ * @deprecated use {@link ReceivePack#getRepository}
*/
- public final Repository getRepository() {
- return db;
- }
+ @Deprecated
+ public abstract Repository getRepository();
/**
* Get the RevWalk instance used by this connection.
*
* @return the RevWalk instance used by this connection.
+ * @deprecated use {@link ReceivePack#getRevWalk}
*/
- public final RevWalk getRevWalk() {
- return walk;
- }
+ @Deprecated
+ public abstract RevWalk getRevWalk();
/**
* Get refs which were advertised to the client.
*
* @return all refs which were advertised to the client, or null if
* {@link #setAdvertisedRefs(Map, Set)} has not been called yet.
+ * @deprecated use {@link ReceivePack#getAdvertisedRefs}
*/
- public final Map<String, Ref> getAdvertisedRefs() {
- return refs;
- }
+ @Deprecated
+ public abstract Map<String, Ref> getAdvertisedRefs();
/**
* Set the refs advertised by this ReceivePack.
@@ -464,25 +461,10 @@ public abstract class BaseReceivePack {
* explicit set of additional haves to claim as advertised. If
* null, assumes the default set of additional haves from the
* repository.
+ * @deprecated use {@link ReceivePack#setAdvertisedRefs}
*/
- public void setAdvertisedRefs(Map<String, Ref> allRefs, Set<ObjectId> additionalHaves) {
- refs = allRefs != null ? allRefs : db.getAllRefs();
- refs = refFilter.filter(refs);
- advertisedHaves.clear();
-
- Ref head = refs.get(Constants.HEAD);
- if (head != null && head.isSymbolic())
- refs.remove(Constants.HEAD);
-
- for (Ref ref : refs.values()) {
- if (ref.getObjectId() != null)
- advertisedHaves.add(ref.getObjectId());
- }
- if (additionalHaves != null)
- advertisedHaves.addAll(additionalHaves);
- else
- advertisedHaves.addAll(db.getAdditionalHaves());
- }
+ @Deprecated
+ public abstract void setAdvertisedRefs(Map<String, Ref> allRefs, Set<ObjectId> additionalHaves);
/**
* Get objects advertised to the client.
@@ -1310,7 +1292,7 @@ public abstract class BaseReceivePack {
if (firstPkt) {
firstPkt = false;
- FirstLine firstLine = new FirstLine(line);
+ FirstCommand firstLine = FirstCommand.fromLine(line);
enabledCapabilities = firstLine.getCapabilities();
line = firstLine.getLine();
enableCapabilities();
@@ -1606,7 +1588,7 @@ public abstract class BaseReceivePack {
throw new MissingObjectException(o, o.getType());
}
- if (o instanceof RevBlob && !db.hasObject(o))
+ if (o instanceof RevBlob && !db.getObjectDatabase().has(o))
throw new MissingObjectException(o, Constants.TYPE_BLOB);
}
checking.endTask();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
index 4b20f6c8b0..84a0972723 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
@@ -50,6 +50,7 @@ package org.eclipse.jgit.transport;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.BufferedInputStream;
+import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
@@ -165,9 +166,13 @@ class BundleFetchConnection extends BaseFetchConnection {
while (!done) {
bin.mark(hdrbuf.length);
final int cnt = bin.read(hdrbuf);
+ if (cnt < 0) {
+ throw new EOFException(JGitText.get().shortReadOfBlock);
+ }
int lf = 0;
- while (lf < cnt && hdrbuf[lf] != '\n')
+ while (lf < cnt && hdrbuf[lf] != '\n') {
lf++;
+ }
bin.reset();
IO.skipFully(bin, lf);
if (lf < cnt && hdrbuf[lf] == '\n') {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
index 211707e9ad..681ae125cb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -181,7 +181,7 @@ class FetchProcess {
ObjectId id = r.getPeeledObjectId();
if (id == null)
id = r.getObjectId();
- if (transport.local.hasObject(id))
+ if (localHasObject(id))
wantTag(r);
}
@@ -393,6 +393,18 @@ class FetchProcess {
}
}
+ private boolean localHasObject(ObjectId id) throws TransportException {
+ try {
+ return transport.local.getObjectDatabase().has(id);
+ } catch (IOException err) {
+ throw new TransportException(
+ MessageFormat.format(
+ JGitText.get().readingObjectsFromLocalRepositoryFailed,
+ err.getMessage()),
+ err);
+ }
+ }
+
private Collection<Ref> expandAutoFollowTags() throws TransportException {
final Collection<Ref> additionalTags = new ArrayList<>();
final Map<String, Ref> haveRefs = localRefs();
@@ -410,7 +422,7 @@ class FetchProcess {
if (obj == null)
obj = r.getObjectId();
- if (askFor.containsKey(obj) || transport.local.hasObject(obj))
+ if (askFor.containsKey(obj) || localHasObject(obj))
wantTag(r);
else
additionalTags.add(r);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalHttpServerGlue.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalHttpServerGlue.java
index fe7aaf7699..075cc9c113 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalHttpServerGlue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalHttpServerGlue.java
@@ -44,7 +44,7 @@
package org.eclipse.jgit.transport;
/**
- * Internal API to to assist {@code org.eclipse.jgit.http.server}.
+ * Internal API to assist {@code org.eclipse.jgit.http.server}.
* <p>
* <b>Do not call.</b>
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java
index fc22034340..c573d1248f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java
@@ -81,7 +81,7 @@ public interface NonceGenerator {
* such that the pusher cannot forge nonces by pushing to another
* repository at the same time as well and reusing the nonce.
* @param allowSlop
- * If the receiving backend is is able to generate slop. This is
+ * If the receiving backend is able to generate slop. This is
* the case for serving via http protocol using more than one
* http frontend. The client would talk to different http
* frontends, which may have a slight difference of time due to
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
index 49acb4d9d8..2b2795fefb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
@@ -1237,7 +1237,7 @@ public abstract class PackParser {
bAvail -= cnt;
}
- // Ensure at least need bytes are available in in {@link #buf}.
+ // Ensure at least need bytes are available in {@link #buf}.
int fill(Source src, int need) throws IOException {
while (bAvail < need) {
int next = bOffset + bAvail;
@@ -1568,7 +1568,7 @@ public abstract class PackParser {
long inflatedSize) throws IOException;
/**
- * Event notifying the the current object.
+ * Event notifying the current object.
*
*@param info
* object information.
@@ -1616,7 +1616,7 @@ public abstract class PackParser {
AnyObjectId baseId, long inflatedSize) throws IOException;
/**
- * Event notifying the the current object.
+ * Event notifying the current object.
*
*@return object information that must be populated with at least the
* offset.
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 577aaf4e9e..4652c3fda8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.lib.Constants.HEAD;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_PUSH_OPTIONS;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
@@ -53,13 +54,18 @@ import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.UnpackException;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand.Result;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
@@ -93,6 +99,106 @@ public class ReceivePack extends BaseReceivePack {
}
/**
+ * Get the repository this receive completes into.
+ *
+ * @return the repository this receive completes into.
+ */
+ @Override
+ public final Repository getRepository() {
+ return db;
+ }
+
+ /**
+ * Get the RevWalk instance used by this connection.
+ *
+ * @return the RevWalk instance used by this connection.
+ */
+ @Override
+ public final RevWalk getRevWalk() {
+ return walk;
+ }
+
+ /**
+ * Get refs which were advertised to the client.
+ *
+ * @return all refs which were advertised to the client, or null if
+ * {@link #setAdvertisedRefs(Map, Set)} has not been called yet.
+ */
+ @Override
+ public final Map<String, Ref> getAdvertisedRefs() {
+ return refs;
+ }
+
+ /**
+ * Set the refs advertised by this ReceivePack.
+ * <p>
+ * Intended to be called from a
+ * {@link org.eclipse.jgit.transport.PreReceiveHook}.
+ *
+ * @param allRefs
+ * explicit set of references to claim as advertised by this
+ * ReceivePack instance. This overrides any references that may
+ * exist in the source repository. The map is passed to the
+ * configured {@link #getRefFilter()}. If null, assumes all refs
+ * were advertised.
+ * @param additionalHaves
+ * explicit set of additional haves to claim as advertised. If
+ * null, assumes the default set of additional haves from the
+ * repository.
+ */
+ @Override
+ public void setAdvertisedRefs(Map<String, Ref> allRefs, Set<ObjectId> additionalHaves) {
+ refs = allRefs != null ? allRefs : db.getAllRefs();
+ refs = refFilter.filter(refs);
+ advertisedHaves.clear();
+
+ Ref head = refs.get(HEAD);
+ if (head != null && head.isSymbolic()) {
+ refs.remove(HEAD);
+ }
+
+ for (Ref ref : refs.values()) {
+ if (ref.getObjectId() != null) {
+ advertisedHaves.add(ref.getObjectId());
+ }
+ }
+ if (additionalHaves != null) {
+ advertisedHaves.addAll(additionalHaves);
+ } else {
+ advertisedHaves.addAll(db.getAdditionalHaves());
+ }
+ }
+
+ /**
+ * Get the push certificate used to verify the pusher's identity.
+ * <p>
+ * Only valid after commands are read from the wire.
+ *
+ * @return the parsed certificate, or null if push certificates are disabled
+ * or no cert was presented by the client.
+ * @since 4.1
+ */
+ @Override
+ public PushCertificate getPushCertificate() {
+ return pushCert;
+ }
+
+ /**
+ * Set the push certificate used to verify the pusher's identity.
+ * <p>
+ * Should only be called if reconstructing an instance without going through
+ * the normal {@link #recvCommands()} flow.
+ *
+ * @param cert
+ * the push certificate to set.
+ * @since 4.1
+ */
+ @Override
+ public void setPushCertificate(PushCertificate cert) {
+ pushCert = cert;
+ }
+
+ /**
* Gets an unmodifiable view of the option strings associated with the push.
*
* @return an unmodifiable view of pushOptions, or null (if pushOptions is).
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
index 9a67f0f8fe..c34e3b8e61 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
@@ -293,7 +293,7 @@ public class RemoteRefUpdate {
final boolean forceUpdate, final String localName,
final ObjectId expectedOldObjectId) throws IOException {
if (remoteName == null)
- throw new IllegalArgumentException(JGitText.get().remoteNameCantBeNull);
+ throw new IllegalArgumentException(JGitText.get().remoteNameCannotBeNull);
if (srcId == null && srcRef != null)
throw new IOException(MessageFormat.format(
JGitText.get().sourceRefDoesntResolveToAnyObject, srcRef));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index 2fbcaa2928..1d0f836619 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.transport;
+import static java.util.Collections.unmodifiableMap;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
@@ -79,9 +80,11 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -421,7 +424,7 @@ public class UploadPack {
* configured {@link #getRefFilter()}. If null, assumes all refs
* were advertised.
*/
- public void setAdvertisedRefs(Map<String, Ref> allRefs) {
+ public void setAdvertisedRefs(@Nullable Map<String, Ref> allRefs) {
if (allRefs != null)
refs = allRefs;
else
@@ -545,7 +548,7 @@ public class UploadPack {
* custom validator for client want list.
* @since 3.1
*/
- public void setRequestValidator(RequestValidator validator) {
+ public void setRequestValidator(@Nullable RequestValidator validator) {
requestValidator = validator != null ? validator
: new AdvertisedRequestValidator();
}
@@ -579,21 +582,21 @@ public class UploadPack {
* @param advertiseRefsHook
* the hook; may be null to show all refs.
*/
- public void setAdvertiseRefsHook(AdvertiseRefsHook advertiseRefsHook) {
- if (advertiseRefsHook != null)
- this.advertiseRefsHook = advertiseRefsHook;
- else
- this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
+ public void setAdvertiseRefsHook(
+ @Nullable AdvertiseRefsHook advertiseRefsHook) {
+ this.advertiseRefsHook = advertiseRefsHook != null ? advertiseRefsHook
+ : AdvertiseRefsHook.DEFAULT;
}
/**
* Set the protocol V2 hook.
*
* @param hook
+ * the hook; if null no special actions are taken.
* @since 5.1
*/
- public void setProtocolV2Hook(ProtocolV2Hook hook) {
- this.protocolV2Hook = hook;
+ public void setProtocolV2Hook(@Nullable ProtocolV2Hook hook) {
+ this.protocolV2Hook = hook != null ? hook : ProtocolV2Hook.DEFAULT;
}
/**
@@ -608,7 +611,7 @@ public class UploadPack {
* @param refFilter
* the filter; may be null to show all refs.
*/
- public void setRefFilter(RefFilter refFilter) {
+ public void setRefFilter(@Nullable RefFilter refFilter) {
this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
}
@@ -627,7 +630,7 @@ public class UploadPack {
* @param hook
* the hook; if null no special actions are taken.
*/
- public void setPreUploadHook(PreUploadHook hook) {
+ public void setPreUploadHook(@Nullable PreUploadHook hook) {
preUploadHook = hook != null ? hook : PreUploadHook.NULL;
}
@@ -648,7 +651,7 @@ public class UploadPack {
* the hook; if null no special actions are taken.
* @since 4.1
*/
- public void setPostUploadHook(PostUploadHook hook) {
+ public void setPostUploadHook(@Nullable PostUploadHook hook) {
postUploadHook = hook != null ? hook : PostUploadHook.NULL;
}
@@ -659,7 +662,7 @@ public class UploadPack {
* configuration controlling packing parameters. If null the
* source repository's settings will be used.
*/
- public void setPackConfig(PackConfig pc) {
+ public void setPackConfig(@Nullable PackConfig pc) {
this.packConfig = pc;
}
@@ -671,7 +674,7 @@ public class UploadPack {
* repository's settings will be used.
* @since 3.1
*/
- public void setTransferConfig(TransferConfig tc) {
+ public void setTransferConfig(@Nullable TransferConfig tc) {
this.transferConfig = tc != null ? tc : new TransferConfig(db);
if (transferConfig.isAllowTipSha1InWant()) {
setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
@@ -849,22 +852,46 @@ public class UploadPack {
}
/**
- * Read a ref on behalf of the client.
+ * Returns the specified references.
* <p>
- * This checks that the ref is present in the ref advertisement since
- * otherwise the client might not be supposed to be able to read it.
+ * This produces an immutable map containing whatever subset of the
+ * refs named by the caller are present in the supplied {@code refs}
+ * map.
*
- * @param name
- * the unabbreviated name of the reference.
- * @return the requested Ref, or {@code null} if it is not visible or
- * does not exist.
+ * @param refs
+ * Map to search for refs to return.
+ * @param names
+ * which refs to search for in {@code refs}.
+ * @return the requested Refs, omitting any that are null or missing.
+ */
+ @NonNull
+ private static Map<String, Ref> mapRefs(
+ Map<String, Ref> refs, List<String> names) {
+ return unmodifiableMap(
+ names.stream()
+ .map(refs::get)
+ .filter(Objects::nonNull)
+ .collect(toMap(Ref::getName, identity(), (a, b) -> b)));
+ }
+
+ /**
+ * Read refs on behalf of the client.
+ * <p>
+ * This checks whether the refs are present in the ref advertisement
+ * since otherwise the client might not be supposed to be able to
+ * read them.
+ *
+ * @param names
+ * unabbreviated names of references.
+ * @return the requested Refs, omitting any that are not visible or
+ * do not exist.
* @throws java.io.IOException
- * on failure to read the ref or check it for visibility.
+ * on failure to read a ref or check it for visibility.
*/
- @Nullable
- private Ref getRef(String name) throws IOException {
+ @NonNull
+ private Map<String, Ref> exactRefs(List<String> names) throws IOException {
if (refs != null) {
- return refs.get(name);
+ return mapRefs(refs, names);
}
if (!advertiseRefsHookCalled) {
advertiseRefsHook.advertiseRefs(this);
@@ -874,9 +901,10 @@ public class UploadPack {
refFilter == RefFilter.DEFAULT &&
transferConfig.hasDefaultRefFilter()) {
// Fast path: no ref filtering is needed.
- return db.getRefDatabase().exactRef(name);
+ String[] ns = names.toArray(new String[0]);
+ return unmodifiableMap(db.getRefDatabase().exactRef(ns));
}
- return getAdvertisedOrDefaultRefs().get(name);
+ return mapRefs(getAdvertisedOrDefaultRefs(), names);
}
/**
@@ -906,7 +934,7 @@ public class UploadPack {
refFilter == RefFilter.DEFAULT &&
transferConfig.hasDefaultRefFilter()) {
// Fast path: no ref filtering is needed.
- return db.getRefDatabase().getRef(name);
+ return db.getRefDatabase().findRef(name);
}
return RefDatabase.findRef(getAdvertisedOrDefaultRefs(), name);
}
@@ -1042,6 +1070,31 @@ public class UploadPack {
adv.end();
}
+ // Resolves ref names from the request's want-ref lines to
+ // object ids, throwing PackProtocolException if any are missing.
+ private Map<String, ObjectId> wantedRefs(FetchV2Request req)
+ throws IOException {
+ Map<String, ObjectId> result = new TreeMap<>();
+
+ List<String> wanted = req.getWantedRefs();
+ Map<String, Ref> resolved = exactRefs(wanted);
+
+ for (String refName : wanted) {
+ Ref ref = resolved.get(refName);
+ if (ref == null) {
+ throw new PackProtocolException(MessageFormat
+ .format(JGitText.get().invalidRefName, refName));
+ }
+ ObjectId oid = ref.getObjectId();
+ if (oid == null) {
+ throw new PackProtocolException(MessageFormat
+ .format(JGitText.get().invalidRefName, refName));
+ }
+ result.put(refName, oid);
+ }
+ return result;
+ }
+
private void fetchV2() throws IOException {
// Depending on the requestValidator, #processHaveLines may
// require that advertised be set. Set it only in the required
@@ -1074,22 +1127,9 @@ public class UploadPack {
deepenNots.add(ref.getObjectId());
}
- Map<String, ObjectId> wantedRefs = new TreeMap<>();
- for (String refName : req.getWantedRefs()) {
- Ref ref = getRef(refName);
- if (ref == null) {
- throw new PackProtocolException(MessageFormat
- .format(JGitText.get().invalidRefName, refName));
- }
- ObjectId oid = ref.getObjectId();
- if (oid == null) {
- throw new PackProtocolException(MessageFormat
- .format(JGitText.get().invalidRefName, refName));
- }
- // TODO(ifrade): Avoid mutating the parsed request.
- req.getWantIds().add(oid);
- wantedRefs.put(refName, oid);
- }
+ Map<String, ObjectId> wantedRefs = wantedRefs(req);
+ // TODO(ifrade): Avoid mutating the parsed request.
+ req.getWantIds().addAll(wantedRefs.values());
wantIds = req.getWantIds();
boolean sectionSent = false;
@@ -1486,6 +1526,20 @@ public class UploadPack {
}
/**
+ * Returns the filter blob limit for the current request. Valid only after
+ * calling recvWants(). A limit -1 means no limit.
+ *
+ * @return filter blob limit requested by the client, or -1 if no limit
+ * @since 5.3
+ */
+ public long getFilterBlobLimit() {
+ if (currentRequest == null) {
+ throw new RequestNotYetReadException();
+ }
+ return currentRequest.getFilterBlobLimit();
+ }
+
+ /**
* Get the user agent of the client.
* <p>
* If the client is new enough to use {@code agent=} capability that value
@@ -2080,6 +2134,7 @@ public class UploadPack {
: req.getDepth() - 1;
pw.setShallowPack(req.getDepth(), unshallowCommits);
+ @SuppressWarnings("resource") // Ownership is transferred below
DepthWalk.RevWalk dw = new DepthWalk.RevWalk(
walk.getObjectReader(), walkDepth);
dw.setDeepenSince(req.getDeepenSince());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
index 9307914444..2bb58144ba 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
@@ -555,10 +555,10 @@ class WalkFetchConnection extends BaseFetchConnection {
continue;
} finally {
// If the pack was good its in the local repository
- // and Repository.hasObject(id) will succeed in the
- // future, so we do not need this data anymore. If
- // it failed the index and pack are unusable and we
- // shouldn't consult them again.
+ // and Repository.getObjectDatabase().has(id) will
+ // succeed in the future, so we do not need this
+ // data any more. If it failed the index and pack
+ // are unusable and we shouldn't consult them again.
//
try {
if (pack.tmpIdx != null)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
index 6d4df4fbad..5c67253cfc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
@@ -348,7 +348,7 @@ abstract class WalkRemoteObjectDatabase {
/**
* Open a buffered reader around a file.
* <p>
- * This method is suitable for for reading line-oriented resources like
+ * This method is suitable for reading line-oriented resources like
* <code>info/packs</code>, <code>info/refs</code>, and the alternates list.
*
* @return a stream to read from the file. Never null.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index ddf916f41f..90524fedaf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -75,6 +75,7 @@ import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.ignore.FastIgnoreRule;
@@ -1498,9 +1499,18 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
private EolStreamType getEolStreamType(OperationType opType)
throws IOException {
if (eolStreamTypeHolder == null) {
- EolStreamType type=null;
+ EolStreamType type = null;
if (state.walk != null) {
type = state.walk.getEolStreamType(opType);
+ OperationType operationType = opType != null ? opType
+ : state.walk.getOperationType();
+ if (OperationType.CHECKIN_OP.equals(operationType)
+ && EolStreamType.AUTO_LF.equals(type)
+ && hasCrLfInIndex(getDirCacheIterator())) {
+ // If text=auto (or core.autocrlf=true) and the file has
+ // already been committed with CR/LF, then don't convert.
+ type = EolStreamType.DIRECT;
+ }
} else {
switch (getOptions().getAutoCRLF()) {
case FALSE:
@@ -1517,6 +1527,59 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return eolStreamTypeHolder.get();
}
+ /**
+ * Determines whether the file was committed un-normalized. If the iterator
+ * points to a conflict entry, checks the "ours" version.
+ *
+ * @param dirCache
+ * iterator pointing to the current entry for the file in the
+ * index
+ * @return {@code true} if the file in the index is not binary and has CR/LF
+ * line endings, {@code false} otherwise
+ */
+ private boolean hasCrLfInIndex(DirCacheIterator dirCache) {
+ if (dirCache == null) {
+ return false;
+ }
+ // Read blob from index and check for CR/LF-delimited text.
+ DirCacheEntry entry = dirCache.getDirCacheEntry();
+ if (FileMode.REGULAR_FILE.equals(entry.getFileMode())) {
+ ObjectId blobId = entry.getObjectId();
+ if (entry.getStage() > 0
+ && entry.getStage() != DirCacheEntry.STAGE_2) {
+ // Merge conflict: check ours (stage 2)
+ byte[] name = entry.getRawPath();
+ int i = 0;
+ while (!dirCache.eof()) {
+ dirCache.next(1);
+ i++;
+ entry = dirCache.getDirCacheEntry();
+ if (!Arrays.equals(name, entry.getRawPath())) {
+ break;
+ }
+ if (entry.getStage() == DirCacheEntry.STAGE_2) {
+ blobId = entry.getObjectId();
+ break;
+ }
+ }
+ dirCache.back(i);
+ }
+ try (ObjectReader reader = repository.newObjectReader()) {
+ ObjectLoader loader = reader.open(blobId, Constants.OBJ_BLOB);
+ try {
+ return RawText.isCrLfText(loader.getCachedBytes());
+ } catch (LargeObjectException e) {
+ try (InputStream in = loader.openStream()) {
+ return RawText.isCrLfText(in);
+ }
+ }
+ } catch (IOException e) {
+ // Ignore and return false below
+ }
+ }
+ return false;
+ }
+
private boolean isDirectoryIgnored(String pathRel) throws IOException {
final int pOff = 0 < pathOffset ? pathOffset - 1 : pathOffset;
final String base = TreeWalk.pathOf(this.path, 0, pOff);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
index eda8afb10f..eda0fae247 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
@@ -51,6 +51,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.Charset;
+import java.nio.file.AccessDeniedException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
@@ -386,8 +387,9 @@ public class FS_POSIX extends FS {
Integer nlink = (Integer) (Files.getAttribute(lockPath,
"unix:nlink")); //$NON-NLS-1$
if (nlink > 2) {
- LOG.warn("nlink of link to lock file {} was not 2 but {}", //$NON-NLS-1$
- lock.getPath(), nlink);
+ LOG.warn(MessageFormat.format(
+ JGitText.get().failedAtomicFileCreation, lockPath,
+ nlink));
return false;
} else if (nlink < 2) {
supportsUnixNLink = false;
@@ -453,7 +455,8 @@ public class FS_POSIX extends FS {
supportsUnixNLink = false;
}
return token(true, link);
- } catch (UnsupportedOperationException | IllegalArgumentException e) {
+ } catch (UnsupportedOperationException | IllegalArgumentException
+ | AccessDeniedException | SecurityException e) {
supportsUnixNLink = false;
return token(true, link);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
index e88e7a38c6..a07a4fd1a5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
@@ -203,12 +203,13 @@ public class IO {
if (last < 0)
return ByteBuffer.wrap(out, 0, pos);
- @SuppressWarnings("resource" /* java 7 */)
- TemporaryBuffer.Heap tmp = new TemporaryBuffer.Heap(Integer.MAX_VALUE);
- tmp.write(out);
- tmp.write(last);
- tmp.copy(in);
- return ByteBuffer.wrap(tmp.toByteArray());
+ try (TemporaryBuffer.Heap tmp = new TemporaryBuffer.Heap(
+ Integer.MAX_VALUE)) {
+ tmp.write(out);
+ tmp.write(last);
+ tmp.copy(in);
+ return ByteBuffer.wrap(tmp.toByteArray());
+ }
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
index a440cb275c..2081bace2a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
@@ -75,7 +75,9 @@ public final class RawParseUtils {
* UTF-8 charset constant.
*
* @since 2.2
+ * @deprecated use {@link java.nio.charset.StandardCharsets#UTF_8} instead
*/
+ @Deprecated
public static final Charset UTF8_CHARSET = UTF_8;
private static final byte[] digits10;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java
index 08377e6be0..4c60862bf4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java
@@ -144,9 +144,18 @@ public class AutoCRLFInputStream extends InputStream {
}
private boolean fillBuffer() throws IOException {
- cnt = in.read(buf, 0, buf.length);
- if (cnt < 1)
+ cnt = 0;
+ while (cnt < buf.length) {
+ int n = in.read(buf, cnt, buf.length - cnt);
+ if (n < 0) {
+ break;
+ }
+ cnt += n;
+ }
+ if (cnt < 1) {
+ cnt = -1;
return false;
+ }
if (detectBinary) {
isBinary = RawText.isBinary(buf, cnt);
detectBinary = false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
index ff28161a52..280cf7e28c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
@@ -189,9 +189,18 @@ public class AutoLFInputStream extends InputStream {
}
private boolean fillBuffer() throws IOException {
- cnt = in.read(buf, 0, buf.length);
- if (cnt < 1)
+ cnt = 0;
+ while (cnt < buf.length) {
+ int n = in.read(buf, cnt, buf.length - cnt);
+ if (n < 0) {
+ break;
+ }
+ cnt += n;
+ }
+ if (cnt < 1) {
+ cnt = -1;
return false;
+ }
if (detectBinary) {
isBinary = RawText.isBinary(buf, cnt);
detectBinary = false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java
index 9fe01f1d8d..1ad6602fce 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java
@@ -48,8 +48,10 @@ import static java.lang.Integer.numberOfTrailingZeros;
import static java.lang.Integer.rotateLeft;
import static java.lang.Integer.rotateRight;
+import java.text.MessageFormat;
import java.util.Arrays;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.NB;
@@ -497,7 +499,8 @@ public class SHA1 {
if (foundCollision) {
ObjectId id = h.toObjectId();
- LOG.warn("possible SHA-1 collision " + id.name()); //$NON-NLS-1$
+ LOG.warn(MessageFormat.format(JGitText.get().sha1CollisionDetected,
+ id.name()));
throw new Sha1CollisionException(id);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/Sha1CollisionException.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/Sha1CollisionException.java
index dcefe95498..0e5c9192d1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/Sha1CollisionException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/Sha1CollisionException.java
@@ -65,7 +65,7 @@ public class Sha1CollisionException extends RuntimeException {
*/
public Sha1CollisionException(ObjectId id) {
super(MessageFormat.format(
- JGitText.get().sha1CollisionDetected1,
+ JGitText.get().sha1CollisionDetected,
id.name()));
}
}