summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Sohn <matthias.sohn@sap.com>2021-02-25 10:29:07 +0100
committerMatthias Sohn <matthias.sohn@sap.com>2021-02-28 00:58:04 +0100
commitf6597971991e3350df568b0cde05c014dcd69c47 (patch)
treecb61592af3f53da45174beed517b3284d7bd55c6
parent286ad23cb56ffeac77d4bfd03be575358fd5217c (diff)
parent789c0479a9294417db0375cce9f1949fe9052d8c (diff)
downloadjgit-f6597971991e3350df568b0cde05c014dcd69c47.tar.gz
jgit-f6597971991e3350df568b0cde05c014dcd69c47.zip
Merge branch 'master' into next
* master: (143 commits) Prepare 5.11.0-SNAPSHOT builds JGit v5.11.0.202102240950-m3 [releng] japicmp: update last release version IgnoreNode: include path to file for invalid .gitignore patterns FastIgnoreRule: include bad pattern in log message init: add config option to set default for the initial branch name init: allow specifying the initial branch name for the new repository Fail clone if initial branch doesn't exist in remote repository GPG: fix reading unprotected old-format secret keys Update Orbit to S20210216215844 Add missing bazel dependency for o.e.j.gpg.bc.test GPG: handle extended private key format dfs: handle short copies [GPG] Provide a factory for the BouncyCastleGpgSigner Fix boxing warnings GPG: compute the keygrip to find a secret key GPG signature verification via BouncyCastle Post commit hook failure should not cause commit failure Allow to define additional Hook classes outside JGit GitHook: use default charset for output and error streams ... Change-Id: I689f4070e79f4a0ac1c02b35698ccaab68ad2f34
-rw-r--r--.bazelversion2
-rw-r--r--BUILD2
-rw-r--r--WORKSPACE81
-rw-r--r--lib/BUILD7
-rw-r--r--org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs4
-rw-r--r--org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF1
-rw-r--r--org.eclipse.jgit.archive/.settings/.api_filters11
-rw-r--r--org.eclipse.jgit.archive/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF1
-rw-r--r--org.eclipse.jgit.archive/resources/org/eclipse/jgit/archive/internal/ArchiveText.properties1
-rw-r--r--org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/BaseFormat.java36
-rw-r--r--org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java8
-rw-r--r--org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java11
-rw-r--r--org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java8
-rw-r--r--org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java7
-rw-r--r--org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/internal/ArchiveText.java1
-rw-r--r--org.eclipse.jgit.benchmarks/.settings/edu.umd.cs.findbugs.core.prefs145
-rw-r--r--org.eclipse.jgit.benchmarks/findBugs/FindBugsExcludeFilter.xml8
-rw-r--r--org.eclipse.jgit.benchmarks/pom.xml28
-rw-r--r--org.eclipse.jgit.gpg.bc.test/.classpath9
-rw-r--r--org.eclipse.jgit.gpg.bc.test/.gitignore1
-rw-r--r--org.eclipse.jgit.gpg.bc.test/.settings/org.eclipse.jdt.core.prefs4
-rw-r--r--org.eclipse.jgit.gpg.bc.test/BUILD21
-rw-r--r--org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF16
-rw-r--r--org.eclipse.jgit.gpg.bc.test/build.properties2
-rw-r--r--org.eclipse.jgit.gpg.bc.test/pom.xml6
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A.asc41
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A.key42
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9.asc42
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9.key45
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11.asc30
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11.keybin0 -> 978 bytes
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/F727FAB884DA3BD402B6E0F5472E108D21033124.asc30
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/F727FAB884DA3BD402B6E0F5472E108D21033124.key32
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool256.asc14
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool384.asc17
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool512.asc19
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/dsa-elgamal.asc44
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/ed25519.asc9
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/faked.key23
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp256.asc14
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp384.asc16
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp521.asc19
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/rsa.asc40
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/secp256k1.asc14
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/x25519.asc13
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocatorTest.java11
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip25519Test.java61
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGripTest.java143
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeysTest.java163
-rw-r--r--org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF24
-rw-r--r--org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF1
-rw-r--r--org.eclipse.jgit.gpg.bc/about.html83
-rw-r--r--org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSignatureVerifierFactory1
-rw-r--r--org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties27
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/BouncyCastleGpgSignerFactory.java34
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java34
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java224
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyPassphrasePrompt.java7
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java388
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifierFactory.java28
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java62
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip.java322
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/OCBPBEProtectionRemoverFactory.java121
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SExprParser.java826
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SXprUtils.java110
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeys.java597
-rw-r--r--org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF24
-rw-r--r--org.eclipse.jgit.http.apache/resources/org/eclipse/jgit/transport/http/apache/internal/HttpApacheText.properties1
-rw-r--r--org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java34
-rw-r--r--org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java91
-rw-r--r--org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/internal/HttpApacheText.java1
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java4
-rw-r--r--org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs4
-rw-r--r--org.eclipse.jgit.http.test/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.jgit.http.test/build.properties2
-rw-r--r--org.eclipse.jgit.http.test/src/org/eclipse/jgit/http/test/RefsUnreadableInMemoryRepository.java11
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AllProtocolsHttpTestCase.java99
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java4
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java11
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java23
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerSslTest.java23
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java556
-rw-r--r--org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java4
-rw-r--r--org.eclipse.jgit.junit.ssh/.settings/edu.umd.cs.findbugs.core.prefs145
-rw-r--r--org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF47
-rw-r--r--org.eclipse.jgit.junit.ssh/findBugs/FindBugsExcludeFilter.xml9
-rw-r--r--org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshBasicTestBase.java71
-rw-r--r--org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestBase.java20
-rw-r--r--org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java66
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SeparateClassloaderTestRunner.java14
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java6
-rw-r--r--org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF10
-rw-r--r--org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.jgit.lfs.server/findBugs/FindBugsExcludeFilter.xml8
-rw-r--r--org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs4
-rw-r--r--org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java270
-rw-r--r--org.eclipse.jgit.lfs/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java72
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java14
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml21
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml21
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml21
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml7
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml21
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml133
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml21
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml14
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target60
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target60
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target60
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target60
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target60
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target60
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target60
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target60
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target96
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19-staging.target (renamed from org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.target)62
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19-staging.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target60
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target60
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target60
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target60
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20201130205003-2020-12.tpd (renamed from org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20201118210000.tpd)4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20210216215844.tpd66
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd36
-rw-r--r--org.eclipse.jgit.packaging/pom.xml10
-rw-r--r--org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs4
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java42
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/InitTest.java21
-rw-r--r--org.eclipse.jgit.pgm/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin1
-rw-r--r--org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties19
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java11
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java46
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java8
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java44
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java2
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java56
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java107
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java4
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/BenchmarkReftable.java54
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java140
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java3
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/VerificationUtils.java56
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs4
-rw-r--r--org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF22
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java56
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java7
-rw-r--r--org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF88
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java167
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java7
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java13
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java2
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java5
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java14
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java2
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java8
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java41
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java35
-rw-r--r--org.eclipse.jgit.ssh.jsch.test/.settings/org.eclipse.jdt.core.prefs4
-rw-r--r--org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/JSchSshProtocol2Test.java92
-rw-r--r--org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/JschSession.java25
-rw-r--r--org.eclipse.jgit.test/.settings/edu.umd.cs.findbugs.core.prefs145
-rw-r--r--org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs4
-rw-r--r--org.eclipse.jgit.test/BUILD6
-rw-r--r--org.eclipse.jgit.test/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.jgit.test/findBugs/FindBugsExcludeFilter.xml9
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java184
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java49
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java88
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java47
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java96
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedObjectReachabilityTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedObjectReachabilityTest.java)3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedReachabilityCheckerTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedReachabilityCheckerTest.java)3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ObjectReachabilityTestCase.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectReachabilityTestCase.java)6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/PedestrianObjectReachabilityTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityTest.java)3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/PedestrianReachabilityCheckerTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianReachabilityCheckerTest.java)3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ReachabilityCheckerTestCase.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ReachabilityCheckerTestCase.java)4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackDescriptionTest.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackSourceTest.java8
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableStackTest.java19
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java72
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java8
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java46
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java28
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java)30
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java20
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java112
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java158
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java95
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java689
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeTest.java270
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitBuilderTest.java8
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java59
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TagBuilderTest.java173
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java264
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java121
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java13
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BasePackConnectionTest.java40
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java50
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java66
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransferConfigTest.java69
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java46
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOTest.java84
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java65
-rw-r--r--org.eclipse.jgit/.settings/.api_filters54
-rw-r--r--org.eclipse.jgit/BUILD2
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF11
-rw-r--r--org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF1
-rw-r--r--org.eclipse.jgit/findBugs/FindBugsExcludeFilter.xml34
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties19
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/ketch/KetchText.properties13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java55
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java32
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java21
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java220
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java46
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java307
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefAlreadyExistsException.java71
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/errors/WrongObjectTypeException.java65
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandRegistry.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/errors/NoPackSignatureException.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/errors/PackMismatchException.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/errors/UnsupportedPackVersionException.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java62
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java39
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java51
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java21
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ElectionRound.java126
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchConstants.java51
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java604
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeaderCache.java93
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchPreReceive.java125
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java758
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchSystem.java320
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchText.java43
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java151
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LeaderSnapshot.java153
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java204
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LogIndex.java95
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Proposal.java415
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ProposalRound.java289
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java293
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaConfig.java214
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaFetchRequest.java79
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaPushRequest.java149
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaSnapshot.java98
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Round.java87
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/StageBuilder.java240
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/TimeIsUncertainException.java27
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/package-info.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/BitmappedObjectReachabilityChecker.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedObjectReachabilityChecker.java)8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/BitmappedReachabilityChecker.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java)11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/PedestrianObjectReachabilityChecker.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityChecker.java)12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/PedestrianReachabilityChecker.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java)8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java20
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectRepresentation.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java11
-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/ByteArrayWindow.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteBufferWindow.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteWindow.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java20
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java23
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java66
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LargePackedWholeObject.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalCachedPack.java20
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectRepresentation.java24
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectToPack.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java227
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java540
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java)11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java516
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackLock.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java73
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/RefCursor.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java50
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java26
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/AlwaysFailUpdate.java72
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Command.java309
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTree.java384
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeBatch.java194
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java354
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeNames.java58
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeRename.java89
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeUpdate.java150
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Scanner.java254
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java42
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java184
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java51
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java102
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java95
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java158
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java71
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectBuilder.java225
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java57
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java137
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java23
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java108
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java397
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java306
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java73
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java72
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpTransport.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java32
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java47
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession2.java45
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java33
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java94
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitAnon.java36
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java69
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java263
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java54
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java29
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory2.java66
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnectionFactory.java73
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/http/NoCheckX509TrustManager.java44
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java26
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java21
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java40
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java86
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java35
-rw-r--r--pom.xml28
383 files changed, 13817 insertions, 10709 deletions
diff --git a/.bazelversion b/.bazelversion
index 1545d96657..fcdb2e109f 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-3.5.0
+4.0.0
diff --git a/BUILD b/BUILD
index be6dd767d5..184ab272d8 100644
--- a/BUILD
+++ b/BUILD
@@ -13,6 +13,8 @@ genrule(
"//org.eclipse.jgit.lfs:jgit-lfs",
"//org.eclipse.jgit.lfs.server:jgit-lfs-server",
"//org.eclipse.jgit.junit:junit",
+ "//org.eclipse.jgit.ssh.apache:ssh-apache",
+ "//org.eclipse.jgit.ssh.jsch:ssh-jsch",
],
outs = ["all.zip"],
cmd = " && ".join([
diff --git a/WORKSPACE b/WORKSPACE
index 7651cba4ee..224968a263 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -27,6 +27,34 @@ load(
"maven_jar",
)
+http_archive(
+ name = "openjdk15_linux_archive",
+ build_file_content = """
+java_runtime(name = 'runtime', srcs = glob(['**']), visibility = ['//visibility:public'])
+exports_files(["WORKSPACE"], visibility = ["//visibility:public"])
+""",
+ sha256 = "0a38f1138c15a4f243b75eb82f8ef40855afcc402e3c2a6de97ce8235011b1ad",
+ strip_prefix = "zulu15.27.17-ca-jdk15.0.0-linux_x64",
+ urls = [
+ "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-linux_x64.tar.gz",
+ "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-linux_x64.tar.gz",
+ ],
+)
+
+http_archive(
+ name = "openjdk15_darwin_archive",
+ build_file_content = """
+java_runtime(name = 'runtime', srcs = glob(['**']), visibility = ['//visibility:public'])
+exports_files(["WORKSPACE"], visibility = ["//visibility:public"])
+""",
+ sha256 = "f80b2e0512d9d8a92be24497334c974bfecc8c898fc215ce0e76594f00437482",
+ strip_prefix = "zulu15.27.17-ca-jdk15.0.0-macosx_x64",
+ urls = [
+ "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-macosx_x64.tar.gz",
+ "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-macosx_x64.tar.gz",
+ ],
+)
+
JMH_VERS = "1.21"
maven_jar(
@@ -83,26 +111,26 @@ maven_jar(
maven_jar(
name = "httpclient",
- artifact = "org.apache.httpcomponents:httpclient:4.5.10",
- sha1 = "7ca2e4276f4ef95e4db725a8cd4a1d1e7585b9e5",
+ artifact = "org.apache.httpcomponents:httpclient:4.5.13",
+ sha1 = "e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada",
)
maven_jar(
name = "httpcore",
- artifact = "org.apache.httpcomponents:httpcore:4.4.12",
- sha1 = "21ebaf6d532bc350ba95bd81938fa5f0e511c132",
+ artifact = "org.apache.httpcomponents:httpcore:4.4.14",
+ sha1 = "9dd1a631c082d92ecd4bd8fd4cf55026c720a8c1",
)
maven_jar(
name = "sshd-osgi",
- artifact = "org.apache.sshd:sshd-osgi:2.4.0",
- sha1 = "fc4551c1eeda35e4671b263297d37d2bca81c4d4",
+ artifact = "org.apache.sshd:sshd-osgi:2.6.0",
+ sha1 = "40e365bb799e1bff3d31dc858b1e59a93c123f29",
)
maven_jar(
name = "sshd-sftp",
- artifact = "org.apache.sshd:sshd-sftp:2.4.0",
- sha1 = "92e1b7d1e19c715efb4a8871d34145da8f87cdb2",
+ artifact = "org.apache.sshd:sshd-sftp:2.6.0",
+ sha1 = "6eddfe8fdf59a3d9a49151e4177f8c1bebeb30c9",
)
maven_jar(
@@ -205,52 +233,59 @@ maven_jar(
maven_jar(
name = "gson",
- artifact = "com.google.code.gson:gson:2.8.2",
- sha1 = "3edcfe49d2c6053a70a2a47e4e1c2f94998a49cf",
+ artifact = "com.google.code.gson:gson:2.8.6",
+ sha1 = "9180733b7df8542621dc12e21e87557e8c99b8cb",
)
-JETTY_VER = "9.4.30.v20200611"
+JETTY_VER = "9.4.36.v20210114"
maven_jar(
name = "jetty-servlet",
artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VER,
- sha1 = "ca3dea2cd34ee88cec017001603af0c9e74781d6",
- src_sha1 = "6908f24428060bd542bddfa3e89e03d0dbbc2a6d",
+ sha1 = "b189e52a5ee55ae172e4e99e29c5c314f5daf4b9",
+ src_sha1 = "3a0fa449772ab0d76625f6afb81f60c32a490613",
)
maven_jar(
name = "jetty-security",
artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VER,
- sha1 = "1a5261f6ad4081ad9e9bb01416d639931d391273",
- src_sha1 = "6ca41b34aa4f84c267603edd4b069122bd5f17d3",
+ sha1 = "42030d6ed7dfc0f75818cde0adcf738efc477574",
+ src_sha1 = "612220a97d45fad3983ccc56b0cb9a271f3fd003",
)
maven_jar(
name = "jetty-server",
artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VER,
- sha1 = "e5ede3724d062717d0c04e4c77f74fe8115c2a6f",
- src_sha1 = "c8b02a47a35c1f083b310cbd202738cf08bc1d55",
+ sha1 = "88a7d342974aadca658e7386e8d0fcc5c0788f41",
+ src_sha1 = "4552c0c6db2948e8557db477b6b48d291006e481",
)
maven_jar(
name = "jetty-http",
artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VER,
- sha1 = "cd6223382e4f82b9ea807d8cdb04a23e5d629f1c",
- src_sha1 = "00520c04b10609b981159b5ca284b5a158c077a9",
+ sha1 = "1eee89a55e04ff94df0f85d95200fc48acb43d86",
+ src_sha1 = "552a784ec789c7ba581c5341ae6d8b6353ed5ace",
)
maven_jar(
name = "jetty-io",
artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VER,
- sha1 = "9c360d08e903b2dbd5d1f8e889a32046948628ce",
- src_sha1 = "dac8f8a3f84afdd3686d36f58b5ccb276961b8ce",
+ sha1 = "84a8faf9031eb45a5a2ddb7681e22c483d81ab3a",
+ src_sha1 = "72d5fc6d909e28f8720394b25babda80805a46b9",
)
maven_jar(
name = "jetty-util",
artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VER,
- sha1 = "39ec6aa4745952077f5407cb1394d8ba2db88b13",
- src_sha1 = "f41f9391f91884a79350f3ad9b09b8e46c9be0ec",
+ sha1 = "925257fbcca6b501a25252c7447dbedb021f7404",
+ src_sha1 = "532e8b66044f4e58ca5da3aec19f02a2f3c16ddd",
+)
+
+maven_jar(
+ name = "jetty-util-ajax",
+ artifact = "org.eclipse.jetty:jetty-util-ajax:" + JETTY_VER,
+ sha1 = "2f478130c21787073facb64d7242e06f94980c60",
+ src_sha1 = "7153d7ca38878d971fd90992c303bb7719ba7a21",
)
BOUNCYCASTLE_VER = "1.65"
diff --git a/lib/BUILD b/lib/BUILD
index 7720696b38..8ad49d450d 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -132,7 +132,10 @@ java_library(
name = "jetty-servlet",
# TODO: This should be testonly but org.eclipse.jgit.pgm depends on it.
visibility = ["//visibility:public"],
- exports = ["@jetty-servlet//jar"],
+ exports = [
+ "@jetty-servlet//jar",
+ "@jetty-util-ajax//jar",
+ ],
)
java_library(
@@ -159,6 +162,7 @@ java_library(
"//org.eclipse.jgit:__pkg__",
"//org.eclipse.jgit.gpg.bc:__pkg__",
"//org.eclipse.jgit.test:__pkg__",
+ "//org.eclipse.jgit.gpg.bc.test:__pkg__",
],
exports = ["@bcpg//jar"],
)
@@ -169,6 +173,7 @@ java_library(
"//org.eclipse.jgit:__pkg__",
"//org.eclipse.jgit.gpg.bc:__pkg__",
"//org.eclipse.jgit.test:__pkg__",
+ "//org.eclipse.jgit.gpg.bc.test:__pkg__",
],
exports = ["@bcprov//jar"],
)
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
index 3dd5840397..b853c6a7ed 100644
--- a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
@@ -51,8 +51,8 @@ org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
index 7ab4b3e0a8..e5aeb3142b 100644
--- a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
@@ -5,3 +5,4 @@ Bundle-SymbolicName: org.eclipse.jgit.ant.source
Bundle-Vendor: Eclipse.org - JGit
Bundle-Version: 6.0.0.qualifier
Eclipse-SourceBundle: org.eclipse.jgit.ant;version="6.0.0.qualifier";roots="."
+
diff --git a/org.eclipse.jgit.archive/.settings/.api_filters b/org.eclipse.jgit.archive/.settings/.api_filters
new file mode 100644
index 0000000000..f4a934aeb9
--- /dev/null
+++ b/org.eclipse.jgit.archive/.settings/.api_filters
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.jgit.archive" version="2">
+ <resource path="src/org/eclipse/jgit/archive/BaseFormat.java" type="org.eclipse.jgit.archive.BaseFormat">
+ <filter id="336658481">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.archive.BaseFormat"/>
+ <message_argument value="COMPRESSION_LEVEL"/>
+ </message_arguments>
+ </filter>
+ </resource>
+</component>
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index 1441f62e1d..428c0a57f5 100644
--- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
@@ -27,3 +27,4 @@ Export-Package: org.eclipse.jgit.archive;version="6.0.0";
org.apache.commons.compress.archivers,
org.osgi.framework",
org.eclipse.jgit.archive.internal;version="6.0.0";x-internal:=true
+
diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
index 7322fd0827..d5cda97e90 100644
--- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
@@ -5,3 +5,4 @@ Bundle-SymbolicName: org.eclipse.jgit.archive.source
Bundle-Vendor: Eclipse.org - JGit
Bundle-Version: 6.0.0.qualifier
Eclipse-SourceBundle: org.eclipse.jgit.archive;version="6.0.0.qualifier";roots="."
+
diff --git a/org.eclipse.jgit.archive/resources/org/eclipse/jgit/archive/internal/ArchiveText.properties b/org.eclipse.jgit.archive/resources/org/eclipse/jgit/archive/internal/ArchiveText.properties
index 3b50bb4fd5..e6e122734a 100644
--- a/org.eclipse.jgit.archive/resources/org/eclipse/jgit/archive/internal/ArchiveText.properties
+++ b/org.eclipse.jgit.archive/resources/org/eclipse/jgit/archive/internal/ArchiveText.properties
@@ -1,3 +1,4 @@
cannotSetOption=Cannot set option: {0}
+invalidCompressionLevel=Invalid compression level: {0}
pathDoesNotMatchMode=Path {0} does not match mode {1}
unsupportedMode=Unsupported mode {0}
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/BaseFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/BaseFormat.java
index 27f001e220..0ebac77228 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/BaseFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/BaseFormat.java
@@ -25,6 +25,11 @@ import org.eclipse.jgit.util.StringUtils;
* @since 4.0
*/
public class BaseFormat {
+ /**
+ * Compression-level for the archive file. Only values in [0-9] are allowed.
+ * @since 5.11
+ */
+ protected static final String COMPRESSION_LEVEL = "compression-level"; //$NON-NLS-1$
/**
* Apply options to archive output stream
@@ -40,6 +45,9 @@ public class BaseFormat {
Map<String, Object> o) throws IOException {
for (Map.Entry<String, Object> p : o.entrySet()) {
try {
+ if (p.getKey().equals(COMPRESSION_LEVEL)) {
+ continue;
+ }
new Statement(s, "set" + StringUtils.capitalize(p.getKey()), //$NON-NLS-1$
new Object[] { p.getValue() }).execute();
} catch (Exception e) {
@@ -49,4 +57,32 @@ public class BaseFormat {
}
return s;
}
+
+ /**
+ * Removes and returns the {@link #COMPRESSION_LEVEL} key from the input map
+ * parameter if it exists, or -1 if this key does not exist.
+ *
+ * @param o
+ * options map
+ * @return The compression level if it exists in the map, or -1 instead.
+ * @throws IllegalArgumentException
+ * if the {@link #COMPRESSION_LEVEL} option does not parse to an
+ * Integer.
+ * @since 5.11
+ */
+ protected int getCompressionLevel(Map<String, Object> o) {
+ if (!o.containsKey(COMPRESSION_LEVEL)) {
+ return -1;
+ }
+ Object option = o.get(COMPRESSION_LEVEL);
+ try {
+ Integer compressionLevel = (Integer) option;
+ return compressionLevel.intValue();
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException(
+ MessageFormat.format(
+ ArchiveText.get().invalidCompressionLevel, option),
+ e);
+ }
+ }
}
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java
index e880f5ec56..940dafd40f 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java
@@ -45,7 +45,13 @@ public final class Tbz2Format extends BaseFormat implements
@Override
public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
Map<String, Object> o) throws IOException {
- BZip2CompressorOutputStream out = new BZip2CompressorOutputStream(s);
+ BZip2CompressorOutputStream out;
+ int compressionLevel = getCompressionLevel(o);
+ if (compressionLevel != -1) {
+ out = new BZip2CompressorOutputStream(s, compressionLevel);
+ } else {
+ out = new BZip2CompressorOutputStream(s);
+ }
return tarFormat.createArchiveOutputStream(out, o);
}
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java
index 859a59d095..72e2439f68 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java
@@ -18,6 +18,7 @@ import java.util.Map;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
+import org.apache.commons.compress.compressors.gzip.GzipParameters;
import org.eclipse.jgit.api.ArchiveCommand;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
@@ -45,7 +46,15 @@ public final class TgzFormat extends BaseFormat implements
@Override
public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
Map<String, Object> o) throws IOException {
- GzipCompressorOutputStream out = new GzipCompressorOutputStream(s);
+ GzipCompressorOutputStream out;
+ int compressionLevel = getCompressionLevel(o);
+ if (compressionLevel != -1) {
+ GzipParameters parameters = new GzipParameters();
+ parameters.setCompressionLevel(compressionLevel);
+ out = new GzipCompressorOutputStream(s, parameters);
+ } else {
+ out = new GzipCompressorOutputStream(s);
+ }
return tarFormat.createArchiveOutputStream(out, o);
}
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java
index 484ab5775c..b16fb6dcbd 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java
@@ -45,7 +45,13 @@ public final class TxzFormat extends BaseFormat implements
@Override
public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
Map<String, Object> o) throws IOException {
- XZCompressorOutputStream out = new XZCompressorOutputStream(s);
+ XZCompressorOutputStream out;
+ int compressionLevel = getCompressionLevel(o);
+ if (compressionLevel != -1) {
+ out = new XZCompressorOutputStream(s, compressionLevel);
+ } else {
+ out = new XZCompressorOutputStream(s);
+ }
return tarFormat.createArchiveOutputStream(out, o);
}
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java
index 59a9765f28..97a24c75cb 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java
@@ -47,7 +47,12 @@ public final class ZipFormat extends BaseFormat implements
@Override
public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
Map<String, Object> o) throws IOException {
- return applyFormatOptions(new ZipArchiveOutputStream(s), o);
+ ZipArchiveOutputStream out = new ZipArchiveOutputStream(s);
+ int compressionLevel = getCompressionLevel(o);
+ if (compressionLevel != -1) {
+ out.setLevel(compressionLevel);
+ }
+ return applyFormatOptions(out, o);
}
/** {@inheritDoc} */
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/internal/ArchiveText.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/internal/ArchiveText.java
index 45f96fa4c1..551646bcdc 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/internal/ArchiveText.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/internal/ArchiveText.java
@@ -28,6 +28,7 @@ public class ArchiveText extends TranslationBundle {
// @formatter:off
/***/ public String cannotSetOption;
+ /***/ public String invalidCompressionLevel;
/***/ public String pathDoesNotMatchMode;
/***/ public String unsupportedMode;
}
diff --git a/org.eclipse.jgit.benchmarks/.settings/edu.umd.cs.findbugs.core.prefs b/org.eclipse.jgit.benchmarks/.settings/edu.umd.cs.findbugs.core.prefs
new file mode 100644
index 0000000000..1c0a3448ae
--- /dev/null
+++ b/org.eclipse.jgit.benchmarks/.settings/edu.umd.cs.findbugs.core.prefs
@@ -0,0 +1,145 @@
+#SpotBugs User Preferences
+#Fri Dec 04 10:39:51 CET 2020
+detectorExplicitSerialization=ExplicitSerialization|true
+detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
+detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
+detectorWrongMapIterator=WrongMapIterator|true
+detectorUnnecessaryMath=UnnecessaryMath|true
+detectorUselessSubclassMethod=UselessSubclassMethod|false
+filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,EXPERIMENTAL,I18N,MALICIOUS_CODE,MT_CORRECTNESS,PERFORMANCE,SECURITY,STYLE|false|15
+detectorURLProblems=URLProblems|true
+detectorIteratorIdioms=IteratorIdioms|true
+detectorMutableEnum=MutableEnum|true
+detectorFindNonShortCircuit=FindNonShortCircuit|true
+detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
+detectorVolatileUsage=VolatileUsage|true
+detectorFindNakedNotify=FindNakedNotify|true
+detectorFindUninitializedGet=FindUninitializedGet|true
+detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true
+detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
+detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
+detectorSwitchFallthrough=SwitchFallthrough|true
+detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
+detectorConfusedInheritance=ConfusedInheritance|true
+detectorSynchronizationOnSharedBuiltinConstant=SynchronizationOnSharedBuiltinConstant|true
+detectorMutableStaticFields=MutableStaticFields|true
+detectorInvalidJUnitTest=InvalidJUnitTest|true
+detectorInfiniteLoop=InfiniteLoop|true
+detectorFindRunInvocations=FindRunInvocations|true
+detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
+detectorXMLFactoryBypass=XMLFactoryBypass|true
+detectorFindOpenStream=FindOpenStream|true
+detectorCheckExpectedWarnings=CheckExpectedWarnings|false
+detectorHugeSharedStringConstants=HugeSharedStringConstants|true
+detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true
+detectorStringConcatenation=StringConcatenation|true
+detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
+detectorFinalizerNullsFields=FinalizerNullsFields|true
+detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
+detectorInefficientToArray=InefficientToArray|false
+detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
+detectorInconsistentAnnotations=InconsistentAnnotations|true
+detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
+detectorInstantiateStaticClass=InstantiateStaticClass|true
+detectorCheckRelaxingNullnessAnnotation=CheckRelaxingNullnessAnnotation|true
+detectorMethodReturnCheck=MethodReturnCheck|true
+detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
+detectorFindDoubleCheck=FindDoubleCheck|true
+detectorFindBadForLoop=FindBadForLoop|true
+detectorDefaultEncodingDetector=DefaultEncodingDetector|true
+detectorFindInconsistentSync2=FindInconsistentSync2|true
+detectorFindSpinLoop=FindSpinLoop|true
+detectorFindMaskedFields=FindMaskedFields|true
+detectorBooleanReturnNull=BooleanReturnNull|true
+detectorFindUnsyncGet=FindUnsyncGet|true
+detectorCrossSiteScripting=CrossSiteScripting|true
+detectorDroppedException=DroppedException|true
+detectorFindDeadLocalStores=FindDeadLocalStores|true
+detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
+detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
+detectorFindRefComparison=FindRefComparison|true
+detectorFindRoughConstants=FindRoughConstants|true
+detectorMutableLock=MutableLock|true
+detectorFindNullDeref=FindNullDeref|true
+detectorFindReturnRef=FindReturnRef|true
+detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
+detectorFindUselessControlFlow=FindUselessControlFlow|true
+detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
+detectorIDivResultCastToDouble=IDivResultCastToDouble|true
+detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true
+detectorFindSelfComparison=FindSelfComparison|true
+detectorFindFloatEquality=FindFloatEquality|true
+detectorFindComparatorProblems=FindComparatorProblems|true
+detectorRepeatedConditionals=RepeatedConditionals|true
+filter_settings_neg=NOISE|
+detectorInefficientMemberAccess=InefficientMemberAccess|false
+detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
+detectorNumberConstructor=NumberConstructor|true
+detectorDontAssertInstanceofInTests=DontAssertInstanceofInTests|true
+detectorFindFinalizeInvocations=FindFinalizeInvocations|true
+detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
+detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true
+detectorFindUnconditionalWait=FindUnconditionalWait|true
+detectorFindTwoLockWait=FindTwoLockWait|true
+detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
+detectorFindUnreleasedLock=FindUnreleasedLock|true
+detectorInefficientIndexOf=InefficientIndexOf|false
+detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
+detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
+detectorOverridingMethodsMustInvokeSuperDetector=OverridingMethodsMustInvokeSuperDetector|true
+detectorWaitInLoop=WaitInLoop|true
+detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true
+detectorBadUseOfReturnValue=BadUseOfReturnValue|true
+detectorFindSqlInjection=FindSqlInjection|true
+detectorUnreadFields=UnreadFields|true
+detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
+detectorFindUselessObjects=FindUselessObjects|true
+detectorBadAppletConstructor=BadAppletConstructor|false
+detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
+detectorSerializableIdiom=SerializableIdiom|true
+detectorNaming=Naming|true
+detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true
+detectorFormatStringChecker=FormatStringChecker|true
+detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
+detectorEmptyZipFileEntry=EmptyZipFileEntry|false
+detectorFindCircularDependencies=FindCircularDependencies|false
+detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
+detectorAtomicityProblem=AtomicityProblem|true
+detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
+detectorInitializationChain=InitializationChain|true
+detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true
+detectorOptionalReturnNull=OptionalReturnNull|true
+detectorStartInConstructor=StartInConstructor|true
+detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
+detectorRedundantConditions=RedundantConditions|true
+effort=default
+detectorRedundantInterfaces=RedundantInterfaces|true
+detectorDuplicateBranches=DuplicateBranches|true
+detectorCheckTypeQualifiers=CheckTypeQualifiers|true
+detectorComparatorIdiom=ComparatorIdiom|true
+detectorFindBadCast2=FindBadCast2|true
+detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
+excludefilter0=findBugs/FindBugsExcludeFilter.xml|true
+detectorBadResultSetAccess=BadResultSetAccess|true
+detectorIncompatMask=IncompatMask|true
+detectorCovariantArrayAssignment=CovariantArrayAssignment|false
+detectorDumbMethodInvocations=DumbMethodInvocations|true
+run_at_full_build=false
+detectorStaticCalendarDetector=StaticCalendarDetector|true
+detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
+detectorVarArgsProblems=VarArgsProblems|true
+detectorInefficientInitializationInsideLoop=InefficientInitializationInsideLoop|false
+detectorCloneIdiom=CloneIdiom|true
+detectorFindHEmismatch=FindHEmismatch|true
+detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
+detectorFindSelfComparison2=FindSelfComparison2|true
+detectorLazyInit=LazyInit|true
+detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
+detectorDontUseEnum=DontUseEnum|true
+detectorFindPuzzlers=FindPuzzlers|true
+detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false
+detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
+detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
+detector_threshold=2
+detectorPublicSemaphores=PublicSemaphores|false
+detectorDumbMethods=DumbMethods|true
diff --git a/org.eclipse.jgit.benchmarks/findBugs/FindBugsExcludeFilter.xml b/org.eclipse.jgit.benchmarks/findBugs/FindBugsExcludeFilter.xml
new file mode 100644
index 0000000000..ad63e8f877
--- /dev/null
+++ b/org.eclipse.jgit.benchmarks/findBugs/FindBugsExcludeFilter.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<FindBugsFilter>
+ <!-- Silence warnings in classes generated by apt -->
+ <Match>
+ <Package name="org.eclipse.jgit.benchmarks.generated" />
+ <Bug pattern="DLS_DEAD_LOCAL_STORE" />
+ </Match>
+</FindBugsFilter>
diff --git a/org.eclipse.jgit.benchmarks/pom.xml b/org.eclipse.jgit.benchmarks/pom.xml
index 5941f13642..e5954e54d7 100644
--- a/org.eclipse.jgit.benchmarks/pom.xml
+++ b/org.eclipse.jgit.benchmarks/pom.xml
@@ -51,6 +51,26 @@
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <version>3.0.0-M3</version>
+ <executions>
+ <execution>
+ <id>enforce-maven</id>
+ <goals>
+ <goal>enforce</goal>
+ </goals>
+ <configuration>
+ <rules>
+ <requireMavenVersion>
+ <version>3.6.3</version>
+ </requireMavenVersion>
+ </rules>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
@@ -95,7 +115,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
- <version>3.2.1</version>
+ <version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
@@ -157,19 +177,19 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
- <version>3.8.2</version>
+ <version>3.9.1</version>
<dependencies>
<dependency><!-- add support for ssh/scp -->
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-ssh</artifactId>
- <version>3.3.4</version>
+ <version>3.4.2</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
- <version>3.0.0-M3</version>
+ <version>3.0.0-M5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
diff --git a/org.eclipse.jgit.gpg.bc.test/.classpath b/org.eclipse.jgit.gpg.bc.test/.classpath
index f08af0a4e9..0acccbaaec 100644
--- a/org.eclipse.jgit.gpg.bc.test/.classpath
+++ b/org.eclipse.jgit.gpg.bc.test/.classpath
@@ -2,10 +2,15 @@
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="tst">
+ <classpathentry kind="src" output="bin-tst" path="tst">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
- <classpathentry kind="output" path="bin"/>
+ <classpathentry kind="src" output="bin-tst" path="tst-rsrc">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="bin-tst"/>
</classpath>
diff --git a/org.eclipse.jgit.gpg.bc.test/.gitignore b/org.eclipse.jgit.gpg.bc.test/.gitignore
index 934e0e06ff..8b6760c93c 100644
--- a/org.eclipse.jgit.gpg.bc.test/.gitignore
+++ b/org.eclipse.jgit.gpg.bc.test/.gitignore
@@ -1,2 +1,3 @@
/bin
+/bin-tst
/target
diff --git a/org.eclipse.jgit.gpg.bc.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.gpg.bc.test/.settings/org.eclipse.jdt.core.prefs
index 822846c4d0..cba893f04e 100644
--- a/org.eclipse.jgit.gpg.bc.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.gpg.bc.test/.settings/org.eclipse.jdt.core.prefs
@@ -51,8 +51,8 @@ org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.gpg.bc.test/BUILD b/org.eclipse.jgit.gpg.bc.test/BUILD
index 1e3677d929..925536e5d5 100644
--- a/org.eclipse.jgit.gpg.bc.test/BUILD
+++ b/org.eclipse.jgit.gpg.bc.test/BUILD
@@ -1,4 +1,9 @@
load(
+ "@com_googlesource_gerrit_bazlets//tools:genrule2.bzl",
+ "genrule2",
+)
+load("@rules_java//java:defs.bzl", "java_import")
+load(
"@com_googlesource_gerrit_bazlets//tools:junit.bzl",
"junit_tests",
)
@@ -8,7 +13,23 @@ junit_tests(
srcs = glob(["tst/**/*.java"]),
tags = ["bc"],
deps = [
+ "//lib:bcpg",
+ "//lib:bcprov",
"//lib:junit",
+ "//org.eclipse.jgit:jgit",
"//org.eclipse.jgit.gpg.bc:gpg-bc",
+ "//org.eclipse.jgit.gpg.bc.test:tst_rsrc",
],
)
+
+java_import(
+ name = "tst_rsrc",
+ jars = [":tst_rsrc_jar"],
+)
+
+genrule2(
+ name = "tst_rsrc_jar",
+ srcs = glob(["tst-rsrc/**"]),
+ outs = ["tst_rsrc.jar"],
+ cmd = "o=$$PWD/$@ && tar cf - $(SRCS) | tar -C $$TMP --strip-components=2 -xf - && cd $$TMP && zip -qr $$o .",
+)
diff --git a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
index 727e0cbc3a..187c583fab 100644
--- a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
@@ -7,8 +7,18 @@ Bundle-Version: 6.0.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.gpg.bc.internal;version="[6.0.0,6.1.0)",
- org.junit;version="[4.13,5.0.0)"
-Export-Package: org.eclipse.jgit.gpg.bc.internal;x-internal:=true
+Import-Package: org.bouncycastle.jce.provider;version="[1.65.0,2.0.0)",
+ org.bouncycastle.openpgp;version="[1.65.0,2.0.0)",
+ org.bouncycastle.openpgp.operator;version="[1.65.0,2.0.0)",
+ org.bouncycastle.openpgp.operator.jcajce;version="[1.65.0,2.0.0)",
+ org.bouncycastle.util.encoders;version="[1.65.0,2.0.0)",
+ org.eclipse.jgit.gpg.bc.internal;version="[6.0.0,6.1.0)",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="[6.0.0,6.1.0)",
+ org.eclipse.jgit.util.sha1;version="[6.0.0,6.1.0)",
+ org.junit;version="[4.13,5.0.0)",
+ org.junit.runner;version="[4.13,5.0.0)",
+ org.junit.runners;version="[4.13,5.0.0)"
+Export-Package: org.eclipse.jgit.gpg.bc.internal;x-internal:=true,
+ org.eclipse.jgit.gpg.bc.internal.keys;x-internal:=true
Require-Bundle: org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
org.hamcrest.library;bundle-version="[1.1.0,2.0.0)"
diff --git a/org.eclipse.jgit.gpg.bc.test/build.properties b/org.eclipse.jgit.gpg.bc.test/build.properties
index 9ffa0caf78..e36d6667ee 100644
--- a/org.eclipse.jgit.gpg.bc.test/build.properties
+++ b/org.eclipse.jgit.gpg.bc.test/build.properties
@@ -1,5 +1,5 @@
source.. = tst/
-output.. = bin/
+output.. = bin-tst/
bin.includes = META-INF/,\
.,\
plugin.properties
diff --git a/org.eclipse.jgit.gpg.bc.test/pom.xml b/org.eclipse.jgit.gpg.bc.test/pom.xml
index fd477b4631..aa09d3f003 100644
--- a/org.eclipse.jgit.gpg.bc.test/pom.xml
+++ b/org.eclipse.jgit.gpg.bc.test/pom.xml
@@ -85,6 +85,12 @@
<sourceDirectory>src/</sourceDirectory>
<testSourceDirectory>tst/</testSourceDirectory>
+ <testResources>
+ <testResource>
+ <directory>tst-rsrc/</directory>
+ </testResource>
+ </testResources>
+
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A.asc
new file mode 100644
index 0000000000..355462c098
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A.asc
@@ -0,0 +1,41 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQGNBGAHBLQBDACsS1vFqE3qgKD2R5X9n90Gz8bucwwvJWIqaHDsVoAtF6IcKIDo
+1hQC9YksTQYl/L7BsMDdmjyEbWRfzW4ory5596d342Hl6g7ZB5jJR5kJJdhy2MCJ
+BUiMy/724Fr/Dz8PNPcEoULz9ZH7HEaPRKqWWEQDUCq5ak0MfLKXtWVUBgsY5Mry
+29d/GLJvnxZ5v16PK+P4oqZ7vh7FWJPlqPK2TCZ6s1rYfWlu9XbHOzwXwozVg7IX
+tfFq4Rij4c0sg0S0GY8hGAlnOpRc/6J2S41Y8p3WqND6r1LPDQUFnNCKXVoHUGeK
+X9U5iAP7pxZSuonsFCqr3CDGxr+kKUpbfZeLrqTA4lBUK7T6w6Wq0qHosCYUU7YC
+GZjlEeCZBRWNfeq45LKlhdNUxHWWgaBsgWaaDmpFWaivblmQGOvmSv1nJMNmedRs
+DSF51nsJnkQceprsvThSa6qJwEYi7pj6L9HO2UGgJLCb3dL5VTQih2gdhghckUSB
+okUkvqBvvdiP2nEAEQEAAbQdVGVzdGVyMiA8dGVzdGVyMkBleGFtcGxlLm9yZz6J
+Ac4EEwEKADgWIQRPCAvglun3I2Bs1iITM2XBzCpwbgUCYAcEtAIbAwULCQgHAwUV
+CgkICwUWAgMBAAIeAQIXgAAKCRATM2XBzCpwboiBC/493+ruANV2eiro8MY8wZ3Y
+gdjp3pHBSg9RK74SIh95J+MW5qzPwkU+vHd8l0+aj9e1sDQb5BFcFk/Z1ioI3TDW
+B4vYWoMkdN932fJ/LcIlhOGjWwSNFZphbYmJzrAwUTA499yx3jt9Dg+vSU88S+8S
+FzYe6CBNt+PqDCbk6Gm+ZcVpR+elq/QJeyhdDzCCrrfNXwPwsVGAM61Z8SvdvNKE
+DA5gHXRsOKf8fu8lqW2Ay0MCvgsZLMIGOMDPCyBUd1bhlU0p18V6D6wdatfzu9gR
+X/k36HJyqB2cHh89/F2KdBSonRVRJOvHc/88zEeRFkiV5pUyrXv40l099+5dvA+2
+h4ODftY7ZbR22k4iX5rqj2BRow3H+N5lTIWgiADPUl+H8z4ZY5G+LWk9Xms3o1G9
+DmEepM3ma1pg4sZbxf0iStikch7aPvL/HgGRPJnDxA/W4KJxqmSw9TTMH/6XHq3D
+ah5Z1lbcylChgrFLFVJi+shnLTZSYttTeKOIqTPi0765AY0EYAcEtAEMANS23tqF
+Dr69wz0AaT7tjoccT/WlSO/gxd80ShMr4vbr21PZp8qGklFmlcrSrMDRwfXY04x2
+qxHR/Kf+hCD5gNvg8kh/yH2lQRcvekzQ4/rLmSXBfGOFg+LioQQ3CZJ1MZyIHzu5
+YVZ2pqALfJwJSw9P5Z340y8sq8AOPaJ+cpIC0rYBp9BUAmz9IeLVT7fUc6CjaWBo
+++E8H+9FyZC71RIPNcCvY+24Qky8ms7nw4hA47Dlht1pqL8dzOggCnohuSYMCXs2
+YPLvDGdZMg7GgQ3AyZawDmjTxFWt51VU5hunGfGiC5Aock8rVHSYsQzUFjVBSR+Y
+Zy+c4noxZD1eRfb8KdFnrewyVqGKFtc/JwA61qhhyYFe5AWMAFtudjGYG0WiTP82
+CmFFc1Qsvyls9G2yMkLuay5wsdIJMnRW9XwBzwxm0mdZI6D3nSbWjPUUfRcGBY8C
+Hqpc736G+UzMevZtorwy/5Q6D8v+Obrk02DIDKa6CJ7g7dTwK0I/fleJlwARAQAB
+iQG2BBgBCgAgFiEETwgL4Jbp9yNgbNYiEzNlwcwqcG4FAmAHBLQCGwwACgkQEzNl
+wcwqcG6mYQv8CFIVGj7/Qnr+wmviMzm8+B4WwQIUHGryqv9hnfp9hLOXMFmNuEDl
+QYkHVChWO7ehrR3fpvpebhcieV19skf/WO8xm0pGSXyjV2/0/bVhXq01xesXHH9r
+4aFxsCu0E8M9fZVAHP7NBr4A67knQ4EHRF6Rwml2ba6Zt2oP15IHvsAq/2B3f8ar
+5sUau4zM1cItG3tg49rbYr6V71HdgkWA22+EkbXL/Qq3haY/er2dIGc73lu8t7oQ
+msGK4LSAGc2661wMvJ6w6feCagkXAtrqyxodhSLoWgF3i0QVQnMbgmYWKEK2B6YA
+g669CZCCXJF+9Ebq+PP/d3Cy/k9iUmWDh72C7iL136kYZt+71b+yOmlDRT9l6DvU
+FP3bhRZWomOt3F3aP5mAdbwrP1NbvlxTYUAf++nUPdpr0Jrvgi67/VHVjaUtVh/K
+gVQ2C+4Cp/fllxXXKQMPcC8dD1x/AL6ytDzPu099ETMULntgbt7A5Lsd/fFScnF3
+ZNx6wjRReIvT
+=8E/K
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A.key b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A.key
new file mode 100644
index 0000000000..afa459c838
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A.key
@@ -0,0 +1,42 @@
+Key: (private-key (rsa (n #00AC4B5BC5A84DEA80A0F64795FD9FDD06CFC6EE730C
+ 2F25622A6870EC56802D17A21C2880E8D61402F5892C4D0625FCBEC1B0C0DD9A3C846D
+ 645FCD6E28AF2E79F7A777E361E5EA0ED90798C947990925D872D8C08905488CCBFEF6
+ E05AFF0F3F0F34F704A142F3F591FB1C468F44AA96584403502AB96A4D0C7CB297B565
+ 54060B18E4CAF2DBD77F18B26F9F1679BF5E8F2BE3F8A2A67BBE1EC55893E5A8F2B64C
+ 267AB35AD87D696EF576C73B3C17C28CD583B217B5F16AE118A3E1CD2C8344B4198F21
+ 1809673A945CFFA2764B8D58F29DD6A8D0FAAF52CF0D05059CD08A5D5A0750678A5FD5
+ 398803FBA71652BA89EC142AABDC20C6C6BFA4294A5B7D978BAEA4C0E250542BB4FAC3
+ A5AAD2A1E8B0261453B6021998E511E09905158D7DEAB8E4B2A585D354C4759681A06C
+ 81669A0E6A4559A8AF6E599018EBE64AFD6724C36679D46C0D2179D67B099E441C7A9A
+ ECBD38526BAA89C04622EE98FA2FD1CED941A024B09BDDD2F955342287681D86085C91
+ 4481A24524BEA06FBDD88FDA71#)(e #010001#)(d
+ #208024A31FF0F6B3E5E91F2ED58572F1A67F1D9AD9290991BF732D1DFFE134E058E5
+ 9BE459478CC5D42058997CF7EC79E55A9CBF10A9AAC761E04A85A5AA0A07DAE61DD0E8
+ 36311534EE606D5392B42D8DEB7824B594280FDB2950D39886B58F0D24CE15F2FF88BA
+ 819B8F4566202B57A9F5C6743862FA80E7429C83CEA57B189ABE4AE657B28DAF7D6EA7
+ 6CA89635B9B6232EE14779452D636B919E707B92B13DA3229133A953DAF021E0928B83
+ 75EDEE98163C2189E22CE9A236C3D0EABD2608DAEF09211B2C77FFE9158A95F8EF2750
+ 221C5ADEDAED0446DC0E4CD8D38AD897D44FA3915934B6CF03F489DFAA6D939AB9F8EF
+ 1C2A0CDCFC3F2207D63A9EB55B09A0A45323D5F59AE4A9D48E819E98E53D04F022905A
+ 9C4D137F32CB33A974F202B0D3AD4AC64CFBA2A4C18650B671AB753D1D3BD7C4FCC8D2
+ 0F85D1876D89A3D94C77423778C08BDF8FBA23A8501D766FC1B4D51F2D4BB4C27B8491
+ CC2595FF54034F4F192D668C1934D292752A4E44C95135D29449B75928BAF1A2389ED9
+ #)(p #00CCD74AC0DC1CC46052F784DB19545A09FF904846247BAD1AFA5E00CE84A4DA
+ BFCD3BCA175508C068553226DBA49EDAFBCC33CF2A914F9006326FCB62C0473B1E93F6
+ DCF89A24006B090834E5686464A8C216B70AD797732E671ED78CD3E922161069E46BA7
+ 489F2A79CE46BDC4E6F5FCE97C3C9DC59399212235C53246609F8B7FDBF2AD191B3FB4
+ 4CC59760BA6D2029541811D1E9B72DC2ADC98513589A9715C82EE88ADF9000A41512C9
+ 6D491D2A30269FBFCD9CF5D2F275A1DBFFEEB72BE5#)(q
+ #00D7532ABA5F442A994ED8290AA71EAAB6F8137FE3F01488298637084157972D31EA
+ E9F495B4360A6E92ABA7C2418A5960DF29B8C8146CC7D1DF1201C17509D7315B6ECF86
+ F0A73C9F5B48D96F2108DD323FAE6BF897A8CB773EDCF5C82E203813945405F414E3F2
+ 99EEDE43EE6259FDED1C01B47C20F67AC488209585FE6FB7D958AF5EF567737E1ACCB4
+ E293724BE8AB6159CD5A6603FFEFC2DBC30FB6EAF647DBE7D9664ED0BBA1C4A2268AE3
+ DE0850572E145BA811EB2087E1E26490F9439D#)(u
+ #00A8026DB6685170EC05DA3574451678718501489843227BCEB772FDB86B20AB2F2A
+ 00B790A2373FD5DF7AD2CAA2E3896C4C9CBA08581F3644DF67743FA4BE1153421F8FB2
+ 47F0EFB64C566CB7E361BAB21CCAF029B43B7172232D11D14912BC035E17FB8BC663CA
+ 6766E6D2531F87AF378896A2AC7D98824AA8F51C5D6C11B7DC266209BCD3B23597F02B
+ A317CCAACC979402F84E47F3D143187F9739FE9A6C71829CC94E4003D70CFFA33AC0FA
+ A12776EFAFFB8261ABE664C6A6FAE623D3678F#)))
+Created: 20210119T161132
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9.asc
new file mode 100644
index 0000000000..362e2108ed
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9.asc
@@ -0,0 +1,42 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQGNBGAHBAUBDADAzIW1FhCcQmP/NDhzXoeRQ+DNACqTed7eEhqm3rowkW4wKi56
+v1UxFR0ZoA3LoT1oQQjiL3IS2l4/qpBR3JQhMFH3pl7yBsCIrN7JvZfAvxq2Ud4e
+YbdonY8mv/yCLq+nkTWlHkWGCppKMm6DupEUw5CFUCiptPXIxikU0uQYB7VRtXhh
+Q1RGdsv6mcOwMIh7hj9flTrX025x0vRypjqgDR05RuM3hMMpJDGMAuf51w/lbRl9
+dAsJzFzg2cf+8qv92Gx3RuP3a3yl6pEuKnkpddC47lj2pvuhWZBf2sXZMyPvMIvA
+Dve4GIVj6k+wXE3DMp0xMy0Fvaxw5OORPxUAKNBR/BgjoRbkbjm+rJZzviu/XVPR
+42+78isvsa+lnEAmTeoTqiz9nlTTPN+6JjwJXYn2LuiFM8XzNPJwNmd/86lW1qbP
+DxZHhD9jjeXZNHUBUCKTIj2Rs2uFa0xrALdhhhGEao9JlVcUqz/Tw85qC+DlzSa3
+3re5C6wGe2pnW3sAEQEAAbRGVGVzdGVyICjDhG5kZXJ1bmdlbiBpbiBHUEcga8O2
+bm5lbiBGb2xnZW4gaGFiZW4hKSA8dGVzdGVyQGV4YW1wbGUub3JnPokBzgQTAQoA
+OBYhBPidC43dWzUvOqsRbzx6+72u3pDoBQJgBwQFAhsDBQsJCAcDBRUKCQgLBRYC
+AwEAAh4BAheAAAoJEDx6+72u3pDogeAL/iNn1aKxA7pKmucyuzcbzvUjtcbbqFL8
+lOLdRxkrQNCDMb+wHgGY1UqJ6wsDruhV+TdPLzUXHpCl731MeLxZyIENr4wnjTBf
+Cr4SU8eFUkusVf3aWK3rlk54W50EkfBjMvDVavRKNkVbCWAAwXZ7mTRf3UlWxg+F
+9Sq4j2P/hEZIznKV3y7zXLDYg0OpMZLgbo3si0CD19/1T/8Z9C680qSwyAiPtjRo
+vfJYqZFQc+ZH7j1Hmvg68d2Qwrkg/WMfOGoTLZq/6PQcM5leQBAodcS1t7C8o1JQ
+6D+f2gLHpMfFdUKh9TkmvnKYI20TWUVm9XQLqyAHsOn2vRMUhydcZ8OP103TKmP4
+mbpgiyp4i4S/7XofHSeFBrbdqt73ebubESuZVXNjTuuSUjH8Jq5nHq22ZrmotSd0
+FNwc9qQmwPG/gmOGq3rUdT/KzUVntc66QN/+hMhFDYMRJsjJhhyszvGuuBp6vBzI
+lMZqx5jqOaI9ON0m0o8CKC50sKdJ25G3wLkBjQRgBwQFAQwAqMXwgfSaIM+eSQWs
+xb4Sf2aCr/RZi5wzZz89lSomMcblqtCpuHv9/C1PSd+N/D1yJKzPChbDjHh6B6gc
+4OUvuDKHmxK+oAiURpvR+yJEdbSEYwiBhfAUD6u2q3IfY5PpyyZT3NjZ9EY8FpOX
+wpgdSOdSiZVZfZt0xUPsGbW/xP1yVR1NHYLuZX5P5oTCvyNJyP8zQQmToamJsvzR
+v9r+2sa5di9roe53kZwq24VjIvTDOOE4xoYEXk5UD83u1LA++9Nfdisxxts1bxgj
+w1ThO/IRTyY5y5bKSQPskYFD3eVz+azjVurqhbj6ep69mR2X2gjLCetz6G1l4PU2
+R6wcqVG6gR6XpGFPsN+M2JRtbgKrtMElA8egJIMMpH4hdXYqIqmpe/31zXhClHkO
+99EbBpk6OawZC+MnreQFN4NIK5uO2aLi+3KL1FFnNlFCXkh/8afbt4+6rHcWC6En
+q5W0ZkLZnpjdFOF5NPTinAdei+14gnf2QhIlFLeMHvqiVEXvABEBAAGJAbYEGAEK
+ACAWIQT4nQuN3Vs1LzqrEW88evu9rt6Q6AUCYAcEBQIbDAAKCRA8evu9rt6Q6DcX
+C/4orMX1YBZNJK5hLLdjfk45EsQDfCnhf8H991xd0Rq4VPJP6bvzikSdOn9bTUEz
+AAhA4JnFu9AMTh8ioOA7ZtViIccplFBivsxi3rAVrQvmCfoP2AdHfG/jB6D9uWGs
+MV5/o1p93Hr0ReO73HK6G4Q3FbJOG3fg6wTcMYyyEQrD5g4IQhKiIhudUlSkKUkA
+9hWKlXSLw3Yx/S2Nq5Ye+Pqr4CU7UFOTCsBIH4Ky+6gLTmP6esPx0k8vXLcOjaCk
+ENcLi0OaL/AgfATH/InN3wzrx2AFfU6eQdEG7HS+402eHl0fmWwMGV+SCsLl+2hx
+AguLFwjetuVrroc/d+XeZdTcpr/2vojsr4UgbkH8Pa2KrGIpK7V85nSOeVbpDUru
+tuimIRSxIQ6GDF2c7Ih5yBy+JPV47gppSV/GgHPgrOlebeqy4sytshRiEYw/nJzZ
+LKBaG6gykN+6MeV6+A7c1BlCYpyi2vcyvouU+l3/Z9gR7vY+Oj1eAaxrqeTFf++3
+qnA=
+=03Wd
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9.key b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9.key
new file mode 100644
index 0000000000..cef72f6234
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9.key
@@ -0,0 +1,45 @@
+Key: (protected-private-key (rsa (n #00C0CC85B516109C4263FF3438735E8791
+ 43E0CD002A9379DEDE121AA6DEBA30916E302A2E7ABF5531151D19A00DCBA13D684108
+ E22F7212DA5E3FAA9051DC94213051F7A65EF206C088ACDEC9BD97C0BF1AB651DE1E61
+ B7689D8F26BFFC822EAFA79135A51E45860A9A4A326E83BA9114C390855028A9B4F5C8
+ C62914D2E41807B551B5786143544676CBFA99C3B030887B863F5F953AD7D36E71D2F4
+ 72A63AA00D1D3946E33784C32924318C02E7F9D70FE56D197D740B09CC5CE0D9C7FEF2
+ ABFDD86C7746E3F76B7CA5EA912E2A792975D0B8EE58F6A6FBA159905FDAC5D93323EF
+ 308BC00EF7B8188563EA4FB05C4DC3329D31332D05BDAC70E4E3913F150028D051FC18
+ 23A116E46E39BEAC9673BE2BBF5D53D1E36FBBF22B2FB1AFA59C40264DEA13AA2CFD9E
+ 54D33CDFBA263C095D89F62EE88533C5F334F27036677FF3A956D6A6CF0F1647843F63
+ 8DE5D9347501502293223D91B36B856B4C6B00B7618611846A8F49955714AB3FD3C3CE
+ 6A0BE0E5CD26B7DEB7B90BAC067B6A675B7B#)(e #010001#)(protected
+ openpgp-s2k3-ocb-aes ((sha1 #84A4A8051974D94E#
+ "26420224")#914E6847983627126D1CDF93#)#DEB66FA3201F91591F688F5D2B1B79
+ 39FD75F58A962C227BC739C6F2F814ADE5115BD85B2E55427153CEC82612F0C5BBE8B9
+ 71A0E5A6B796111B6B1A03C4C926825F03B871CBFE0F64BD0F0CC65EA34E718BA823BD
+ 136D78C9E88CA1733DFC8D6A38830274322A589BC522A2A824FCEAF453523CDD9BC391
+ EEF1355470C110E9A92681DA0C61563465D5238CECCA2D6CFA78FFDFBDDA17A308D6E1
+ 3B1858890EF25A7655F22FE6305AA0129DE5A353B657065E608A616A23C6AF561C4472
+ 5AA705E55343E9C728641BB63C64F804F76A4C5008CE5FFBC09F03B632B42180425D28
+ 9DC1201D91B1989627EE5930E6EF2F6606108B2F048934A9D79DB4834DD950C4A2013C
+ A40B50EE54FA9E3CCB20C210244BFACA795494A1FCFF35856ACF63214A0498ED894BAB
+ FE80CC24D8A478AD08D0BE8CDC8F357FB7F28A30B87540B9B4970D6EA0AEEF46A2549F
+ BA43A98FAD75B4228108DB50D1C3654422E24B4C7754673A66281BB283CD6A1EA8E64B
+ 97DC9083C62034BF7FBCD193830F8FEC3589673B864E50EF7AF4DEE046BD26041E2925
+ 170EB7B6DC6060E78309CC8A136AE9CE44D3B4EBDEE4479482464D0D23C13529184021
+ 795557323D353A70CC710EC2A79C66E860095C082E40724867A9ABBFD3407B2F92CB2A
+ D0D95CC8DAC2FB2C0187B3BB09AEDF869AF1969BA641027D4D5DAA31B1DA5822D40A5A
+ 7FAD1C054C02CF8F8F692B1C45C879299C0E9D5E5A165F6C22DEEBB8C16FFB91F381C4
+ 8FCB209657A7BF9268BD34808D0A9D3D6F50F7026BC297FD3A08790B8EB5CC0291246B
+ 16E4B50A7E9630B33F59B5EA24EEA396F07AEFD0C262BE9915CB32D5F03673CBD3D20A
+ 831FDF55B5BA3D03440A8E1A331147A8AE0760EC593EDC881F5F0A04F4FCBC80C1531A
+ 4DE71D014E3612C2C679BEF3AED59F358ABA5731FF80FA15EAD2CB95AC548F6AB0FD7B
+ BBBB2CB63DFFE9E672605B7F54EEA4B4C046C4CC8036F2F76260CF068D232A40F492FF
+ 9648CC7459F0F46FEABA3D62B9F421B0F8A1BF914E41702540213848345498AA13F989
+ 49EFC2888D3720DC34D20634472FC3A194F1403C1609C38A020F7E47F3205CA5C0CB50
+ 26270083ED153BF97375407514BA15D92808A8C10F8160880F6981BE53294292E4EEE8
+ D215E7854FC79016B64984BBBAD2E99EED8D66B25575183C279E4DAFCB63F1067FA2B0
+ 0888A9C226D4846376520720FC1E947A93A1D32444F78B2F4EB836A6F8C685C1D82958
+ E31560C3FC861D2B68B889E1B5EE0476B914DFE316411F750D252F24076E53557AD5F0
+ 4050E5E839B33E5B8AF16FDD9FE033B39796A52DE8AF65375966D4DB137C85C800B5B9
+ 0E29434E4B215DE35E60C85391DFDFA572C6F9747A0EA0964236FBB3B04394D9DF0694
+ 6E4CC9CBCE352382908148D265293C6EADE7C2AED6F5AE#)(protected-at
+ "20210119T160858")))
+Created: 20210119T160837
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11.asc
new file mode 100644
index 0000000000..f412019906
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11.asc
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBF7EL8wBCADO46xh7nXn7vZ5ow2Zdrp7WTh9BlT2wtaHNKpnKvSoYHjJbbGz
+yF8Jf/qVPuXNbjx2df1lT7zT7x3evcjQoNy80deftCw8ApZB9RMOo3uUIqS2VpO+
+cS9rjTgBRFL6xDv3g4++CE9s+5dKE9gKkwleZ5/tVqUIoHPAIUEjpcPHngi5m2bi
+tSmQUYWLGcliR1E79sJMSzPt1neksqHFMJ1KTEJLAABZ0t3PiBzmycIQWThX3uU/
+lcgnZmmhWCJIqV0yRZqxl61ejUfq+zK0T7MzhAAugqe7D6BM1FRwZRNCHwDQXIvt
+/t3fczTe+x9oTy4qX4MfaP8lHM0223MwGR13ABEBAAG0H0EgVSBUaG9yIDxhLnUu
+dGhvckBleGFtcGxlLm9yZz6JAU4EEwEKADgWIQQILQAv4wNQfEJ6I/NEWemKCmiQ
++wUCXsQvzAIbAwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRBEWemKCmiQ+xev
+CACZSWh4xjTgafwGMP9RnReOhubVmfHS+XGlidDQzJDtshDQddPZ3oQwyTe3OgkW
+ZgOrzjrHGsZp3WZmGUZejrKt2Brqp+h+VRujFVcKk4N9A52BkM6OeT9lzBabOpuA
+UaDsNMSFsMcGTTYpB16+sDcyui8LW1jGi1y+8aQa+u1lIk/vVycq8o4htn2Af8xZ
+rAT8peapsjoNjETEs8OQ0al3Q0UX9amW6Rq1zZZ0XtoXDCPTI01EfczDMN+AZoFk
+UYHwSREDFLSh+c+q1HhYp4TqP+2a5Rayna//n7zci1PmSX7zD3iWzV1jEQ3Jm8U3
+DY+P/WLezQdSJIBVCFpCualquQENBF7EL8wBCAC+ef+vNvfu1jl9BXpu6K9PG0I5
+DQfrNtcdPq90O32ipvsYvqGOJX9MHoTyxBPLew+e5UsYb3ex62JyJqdAaqSwYXEN
+MBESZx7yBqBMUvildfh8dowbJeblxCf5KsE4C9uNfg4ApWGD7PjVsUCh47V8VcfG
+ymCxxq80r+4GfFtt/HC+l9fPUnDLuXpAWEM2GPUzcauUoEXxZK6nhstYCRlKlQcK
+Tn+LtCC7SGpYlqvwWBzAnOYP9+eZfSJ897g0AiTEhK0JsBlDAb3UAWHYHkAkVa1+
+oU/UedhPC4j2Q7RzPQFMun6aGkaDrntCxvT7IFiMplPG7iy0JDd6ubrWSzivABEB
+AAGJATYEGAEKACAWIQQILQAv4wNQfEJ6I/NEWemKCmiQ+wUCXsQvzAIbDAAKCRBE
+WemKCmiQ+xoBB/9BAmlHQUmVl/bkwszAcyXkR5HsyA4htMJt+6GKlqftuhLP0SGK
+Il+7GeK6NqNdQXxXG5Wj6dn7ZqWalQRA0evEa6VLH+74zrn0llWfzTPIcP1bHW7l
+uYaOzZ1z/q4FoEGNJxp/jdToZ4970OXLzqY/G/QlMJIlXWCC0EXNYbKCEpOE9uvW
+h4kWe5xeGOmhZylYbzurTDzqEtKy+LZ9f2xNYn6ElcWtwxsxwSY7L9B3eNcCYE46
+Np6uqzPffB9s7PHW46yEL1lQs6ME+9hBGyjeVop+Wg9qkh3YCrp+KY5Vkmdndwkn
+Th4FnTpcCiS06fCVHHC5kelh+H6TgRA+XQ/V
+=WGUq
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11.key b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11.key
new file mode 100644
index 0000000000..b8765aaacb
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11.key
Binary files differ
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/F727FAB884DA3BD402B6E0F5472E108D21033124.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/F727FAB884DA3BD402B6E0F5472E108D21033124.asc
new file mode 100644
index 0000000000..c6e0408b3e
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/F727FAB884DA3BD402B6E0F5472E108D21033124.asc
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBGAHViwBCADaE67a5U8oqNjqg//SmlNLd+MrFIccHsg+pkSDhF6ipjZEXdFu
+oRQ116tH/qY1SzsHw/TMLujYeTBW0KwQ5E2+TWagckL8pJpDt7ZC08CTK6u0Xvjy
+BSqy/t29NPTuQxxxqRx5gq91mtuo8v00fQmqkbFUgkVfEOKOv4qe2tlaR5pTvpmV
+VboXOls87RPgP/X665kamHjsywrsDpZ5FbvPS8E2kKdYXqeHaiEU4i0Bizjx3diK
+ilPEIxxl8zgDsROXyKagCy0KOOajBqhFhStQH1soIzvk8aG/9eItKmTa2v1BD2mV
+UlZNQ9ZfsnXx3QIBLmA3jugH69rkcekHRCWPABEBAAG0HVRlc3RlcjQgPHRlc3Rl
+cjRAZXhhbXBsZS5vcmc+iQFOBBMBCgA4FiEEJJx/+EvV3v35cJ+Jx5r/T8UxegwF
+AmAHViwCGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQx5r/T8Uxegx+hQgA
+pIy+E58aWh9FIHW/POqLB/y3v/GbYdIbzk9ro0FAXQ2tQsoGQbsuLckJVIlyja7n
+oQX23OmWTOc2tj/Kpy9lZ2ButW43FaMSiLh1G/VtM5pqJ9yHFdb1z7Q+LHMhhB7w
+RKOoNSEgkwtxv4LAkKz5t/BrDU0hvDPxWPqCSRvSAEE6qIY0fa3mLf9ijy3gLlCd
+hBayUC53W0tL7gLHgprTM/fX7b1pDab8beorroPHs5XzcYUBleaCGmEYbtV2eXPQ
+5irOXOC/D/E8vOfOZwhOhFOZk9b4UnhFK4OCfpKIzIooc6tlboVPhdx7jb5DkXQo
+rozfavEvAPx8INi9KNmdBrkBDQRgB1YsAQgA6q/O6mAPB0kj3pnpuIdJC8EumgKm
+7W8rv+ZfRGePg+IEEm5DeFKtfWl70YH33nGmwnWB95wO5412JCNC174z5LKDBbz5
+PWT/yTNnmjooxj6G4p/YVwXYJkvfaDP+kQnYgJAybpeqTa30tES0eqvI0J9aQo1h
+GSMRCkE6QMV45IMj6gH9rptQv9e8U6gbnwBPxWPG2FH5rsGIGQGzIEmGxmKRyxXm
+YDU4f7oWPHSg1ikQqCzAxxCBxeCOaid3acLK8//TOwF/Do8GPJbcupEDqsgbFNGM
+BDWtmkmxjrLntlU+dvIPcsBxdBUrrADiJ/k7EfFv3kHfLfdAonSdKZL85wARAQAB
+iQE2BBgBCgAgFiEEJJx/+EvV3v35cJ+Jx5r/T8UxegwFAmAHViwCGwwACgkQx5r/
+T8Uxegy4+AgA1bzFKpsqkwrjZKDCCT759xeuUbxnYE9kBJgFSVuhn7fUbB4MoHx4
+shBptx7iBOdxxT7yC0oaDPhbiIkttb/c5W0f6JuLr08JpjkFfkrWF+dMcVrtXwPx
+i/30ccV98qWJDCBunyeCwBNie1Ck+qXMxm3FYy4qIbftMQ7mG6KKN6eFlbxu8B6M
+p93DFUvycGH9CWz0yJcho7KT0NSSyoLZhJz2uxRe1BwGMV20O9AG9yicsU0/uJxY
+a2Hble8NkH54XDuZkrsBaAb/o8UsWP7AJdYYsb904UZDIZNRfjWapOmODnlnK8Ta
+Q8pyYRGS5of1SapatMfpQZF6hdsamnTH6A==
+=guSE
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/F727FAB884DA3BD402B6E0F5472E108D21033124.key b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/F727FAB884DA3BD402B6E0F5472E108D21033124.key
new file mode 100644
index 0000000000..63617c09bf
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/F727FAB884DA3BD402B6E0F5472E108D21033124.key
@@ -0,0 +1,32 @@
+Key: (protected-private-key (rsa (n #00DA13AEDAE54F28A8D8EA83FFD29A534B
+ 77E32B14871C1EC83EA64483845EA2A636445DD16EA11435D7AB47FEA6354B3B07C3F4
+ CC2EE8D8793056D0AC10E44DBE4D66A07242FCA49A43B7B642D3C0932BABB45EF8F205
+ 2AB2FEDDBD34F4EE431C71A91C7982AF759ADBA8F2FD347D09AA91B15482455F10E28E
+ BF8A9EDAD95A479A53BE999555BA173A5B3CED13E03FF5FAEB991A9878ECCB0AEC0E96
+ 7915BBCF4BC13690A7585EA7876A2114E22D018B38F1DDD88A8A53C4231C65F33803B1
+ 1397C8A6A00B2D0A38E6A306A845852B501F5B28233BE4F1A1BFF5E22D2A64DADAFD41
+ 0F699552564D43D65FB275F1DD02012E60378EE807EBDAE471E90744258F#)(e
+ #010001#)(protected openpgp-s2k3-ocb-aes ((sha1 #3E87FF0806B054D6#
+ "26420224")#7E8282B83522F91C53D76805#)#70B1997D514FF5800155A90FD785E7
+ 7DC2D782C48FC9BE44D0192C0AD56804468C910A202191EAD077B5542C95FE72BCC450
+ 0C2A8E8313C0CBD6C881236AC13E0BE893663946B5AABBDA57FFE4BA49973D547FA5DD
+ 1236DEF9FA5A9CE52F7AF1947F42A6C3502A47E8EF7E8CEFDDD44D0BFE090EA3220C2B
+ 52E11776DE36DD6C72D3B39A56F5D7295D26A69DB8CDAD1ABDBE1B21C1B754C9184E65
+ 2CAE169E2F492FA0EE5908AC5CB3BE5F4C7F6CD9F41314D1BD9B1DE713A4E6C7DFB11D
+ 2E64000ECFBBC89326B1322A8A227ECE7B919408C9187B5C9D53FC3985833E76D72164
+ 40B7386569E4DE270C3616ABD2A91A657AC58AA872704CB3DD4DF08C77D03D8E3CEDB5
+ 0D83BC3837FFE45D64B457AFE9A6ABF680637C51F80CB54691233BC4DE640026ACDAAC
+ 3FC0749FA8353F6EAD5D362A63C1CF25ACA73A9CF3290B54C18DB3214AF078D918682E
+ 513C434EFA06D9045571B1734CAE42990A1BE962D6E2A45846169EAAF2CDBD520813C2
+ D4DE97FFFABE582A02CA893F91EAF0EBCDCEB70B35850FDDF56EEA60C845A7E5C052C4
+ 33344776E7A4C787690CC0E13F32373EE425CA10520C251D045C0AE73EE7A0CA83C858
+ E2E528CDBD117BC022ED3F5DDE40CED0128B761E29B11F422C8E7C4281BF94F6F75D07
+ 0EE58426137548ABA38019A34DF1A66F700C29EF5545AC88BED75B5036801F0D8D4DB9
+ C6CCEA83D9DE3D626A04A80F218EFE9C74C173412A8A86786AB4A85403E8F8292CFED5
+ B8BA72FB5CE1BDD094AD9D633FD482F8FDBFA540DD2224149786ADA8DB6310A7C0C6E5
+ 9167815B2CEF34E7C458C41B5C56A79414BA57073E9B06D28CA08C56ED5E685EEA2BA5
+ DD112F87B253A0D02AC7CF93EDE93F48A80B2DB57B254937EA80E9AC1CEBD36FD297EB
+ C8A3B42CBC3D2CA732891B49457F3F15AA3F9BF93553968A07CB1A834B392F27B2D152
+ 47D93E46A6338694EA53CA0F5968109B4FAC9A#)(protected-at
+ "20210119T215925")))
+Created: 20210119T215908
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool256.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool256.asc
new file mode 100644
index 0000000000..8427cfcc05
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool256.asc
@@ -0,0 +1,14 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mFMEWsODvRMJKyQDAwIIAQEHAgMEQLOxiiHZ/V6v3kvrhbnRtTp+oOPVpuvDKOiy
+gJOCZ7EWMVAwTr4syaSh8W8hdRgZ85Evv/1PYNFovYb6vzgVr7QJZWNjLWJwMjU2
+iJQEExMIADwCGwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4DAheAFiEEBjPF9yoZ
+j1HmUOSr0Mij2vngY0oFAlxVsKIACgkQ0Mij2vngY0pARAD/RozGDidH/0aFlxeU
+VWNJKjPiax6vdHqur5dqBS/RhhIA/1sPUnyAIvAXXID1uhK6oIBRKi7WJ5rI7vSy
+rBR5MlNJuFcEWsODvRIJKyQDAwIIAQEHAgMEE9Vd8dIjHJkmRs/8MLz4Krfwz5BK
+hunq1T0xnp65OEZJd00VxA+VUXdEUHfaDehtSv7izCpq4lbXGCkEGFN7QwMBCAeI
+eAQYEwgAIAIbDBYhBAYzxfcqGY9R5lDkq9DIo9r54GNKBQJcVbCpAAoJENDIo9r5
+4GNK0MYA/2p5cq5smjSvKD/EGkosQwfcqkeygsQuEpDDLeEdsv8vAP9j+RHKX2tl
+W08zbayxGB0E+aCHuKCF8iLPeI4eroi/fw==
+=vsa4
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool384.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool384.asc
new file mode 100644
index 0000000000..bdb20fe939
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool384.asc
@@ -0,0 +1,17 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mHMEWsOEUBMJKyQDAwIIAQELAwMEivcvlPJsPmivhJcrfHx+ORxyum57GtRhWM49
+Yr8fJ48gyFqj9cLYOBdhEVvcfceyBPXmyt0TozWtjkGzgbF4LIvN1EB0DW0Rlsdn
+p72/hf0gnXvWZdD8euArX4RaAYuQtAllY2MtYnAzODSItAQTEwkAPAIbAwULCQgH
+AgMiAgEGFQoJCAsCBBYCAwECHgMCF4AWIQRbiiVMgjztmN7NEO1s8tzoVZmtogUC
+XFWwugAKCRBs8tzoVZmtoj1yAX9P1UV7FYpGUIP13aPP0d5Bx8HdQDAoexdXz3WW
+WPL/7OhSjPde23Q8TfgWyO21M2wBf1oWjOsDSjO5mDLCr7ypAFF6IJAgx76tSUe9
+Qmy7sL94OWDQ4+1Dccnc9GGiHLtRI7h3BFrDhFASCSskAwMCCAEBCwMDBETUkqGr
+7p8kX2dm38jzzxXRh1+OL7nmY168Zeo9yfwDbnyx8BoihP9ZgPWjGXmefT78GSfw
+ZDaYgC2NFQOcI/b8agh3PcjrXgZaFCZbUR9v2DnLUpCF8ZbxDJwEqweNTAMBCQmI
+mAQYEwkAIAIbDBYhBFuKJUyCPO2Y3s0Q7Wzy3OhVma2iBQJcVbDCAAoJEGzy3OhV
+ma2ig1IBfifduIiwdAlD45MOolSpHMX0AT7KoJHpt9ZFvWnjQkq9ZGEA/RA9vx7Z
+sLb7IsG1GgF/Sn+gtf/JIteXaZMnOhEOZ2oFUufij6o8gII8/9s8mkIjkrIICy//
+0n3q82ndFg0c
+=fcpz
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool512.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool512.asc
new file mode 100644
index 0000000000..5b4bca2c67
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool512.asc
@@ -0,0 +1,19 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mJMEWsOEhxMJKyQDAwIIAQENBAMEA28ylDnn3LR7t6EQow5DszCpmUA+yup03LsT
+9o0Tw4/asi8nAz+1tlRY5HD49j53PziOlsGzKYa/jxGWjhVESgqLrJp/Eo65zK9v
+yDhX+iCkSYQ15WGKr3QaRUmBOUbX9PqE6dY+DDGQ1kewI93QIGCB1gn+OSmyKPm3
+YaVIbo60CWVjYy1icDUxMojUBBMTCgA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgID
+AQIeAwIXgBYhBExZq5JyqmofYLhb0KpcWNFPe49IBQJcVbDYAAoJEKpcWNFPe49I
+F8UB/i8DwypbNusdbAqTbu1Twpn/QFMaVKHn8Ysgzqpimv+6hRq7uzekCvEOPOjl
+Oke5yLp8bpTTMPRKyfjNatQhdn8B/2+54qtXJuQd9oTSz7f2eFYvA8ZsMQgApYNl
+ksvKSw6dhSNX/DXK7JYIlgZXx7UGTkP4h3FQSiyCUJhVpClVVGa4lwRaw4SHEgkr
+JAMDAggBAQ0EAwRCtEqQkEgzQDxNGCj0duj0aGvnH+DHKlP4V6p9LJVIL0TuF1uZ
+BzP04efRvZT2vzCTcvvdE/G6G/txEZsR/989OchbkVWOPM/oCVktkaA02rBMcefh
+k9wKD+O9E3oHEN+tBt3yhmsv0MIR9IfPwi1GCsu55p4WUI//+ysB2T0YaQMBCgmI
+uAQYEwoAIAIbDBYhBExZq5JyqmofYLhb0KpcWNFPe49IBQJcVbDgAAoJEKpcWNFP
+e49IZQUB/R4U4aWBMimZSL8kyaK+/Y8NcInIGqRzrm5zPnTSHrgQeH55SVKahzsq
+j57D1Ec1HnUd4ctISVocOxpUfnJq5NAB/1fzbh+1RN2ZyNW6tAJlA/Irkwzzbil9
+6fAIvRolwwaGsUZNMEiCF3rTcFaenJg9UhQvX6BoqXCdwawqTZCRN6E=
+=h+On
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/dsa-elgamal.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/dsa-elgamal.asc
new file mode 100644
index 0000000000..db067325cb
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/dsa-elgamal.asc
@@ -0,0 +1,44 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQMuBFrDf8YRCACHbPXT8jG3RNWdNms9xvdaiLrY+Iui1Gq2WGLSajPEZVASEWN+
+JuuX8k9d05rb+F2VAqLnW3CQreDW6unVNeRf52tdM8J4eXmeu/Bkk8y1Qx/HbGca
+sAGSIEKg34TuV5Ly5m4Z07bs3HPYUUQbmu0uclGfnX/ArZ+4Jp+uypC9bErdiXM0
+cM7d52tb9IvOlXNu23rzHDbgVP6qF/AxLSRD8SQPvshu3/5b0bvdBkHVk+dHoLO0
+fC5j476ibHGGZcnPMrTwqEIAxUCy5wQ3Lb/2/G31kuV55bAZ41tUNEvfzbiRN1L5
+1uiO+XX96bRqLN13t0Coaba9fq1aN5Zr6piXAQCuNzvj8aaLXAOEXVRej6a2k+/C
+Jny91MgjSM701twUDQf/RMWHwQuFPe6zSDQs4pWlxkHwXJw3AVidkoWg/DCwv2pJ
+5VYQxBXRwND2OhcZvmeDT94UzPws0dFbprWyymtA49ZXitPGzFARAFWHWxk/IsOf
+Idc6w5eHXDMHxLhiPFqfjKeNpibzO2P7LXP2bUKzwybkKZarz1N6pfanDXAtC4DU
+SC3qWNqywYlfINAGCdwsPu5qFUNSnkjTYxe/MiHb4kL1p/z8qFNWrvg6GryXygp5
+cLdqckjPaUHlR+B9wQZIVRzVdlFAbMDJ0EERLFG7FbIuY8dzy5x7n+oBOgRxee2I
+ytUpGVMLIJuecARLXNKsMXviCMYVE7Tj5hiSoM0TIgf8CwLLFsSa0EDm/wlXYZMj
+2gg3Z8iCz6ycxvFD9PXNt+8jyELO8CwS2pWu7ptBgaugkinqd40EQslQoP76CcHq
+bGQEohm4SnmfGsV8dicuziMVVKkVrYgbGvZ5cQ/ONGTTnSJuiTPN19oztwh8JOEc
+Jd4l+wFuVSm8OS1mj5eexeX1Tz3NfWQMT4deKh+jiTLe9Sw/57sSjxiw/8IczqhN
+Fu3YIy40d3Bv+OF6i8I+94WLbJPiX1ban1wqcA0bMaps1aYVtTRZ+mP0b3M9W7qa
+383/SLCBjUzQ7zm6PX/7uAXFyZOQfcyLaJ8Hc34yOE0git1DWmRS7U16Zv54v1Uh
+HbQGZHNhLWVniJQEExEIADwCGwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4DAheA
+FiEECRxEzpz7w/9+x6ZNyKEKfXgnPhAFAlxVr2sACgkQyKEKfXgnPhABkgD7BfEd
+jhB+ApC9icNLs893i2jiHbAxZGSOQMRhCaJ7AzoBAIipcSIsBa3LJ4eTec1esiCY
+a8xzxquCTA+oANNoX7p6uQMNBFrDf8YQDADMPZ6+/YAIjXMLfQKX80jz6FZ4Kdfx
+Dc60m4O+ZElMv7eXtQJC2L/xOh4Th1fZUQIhSdtFiwaCSkCD8occfJwyt+lH3Dj0
+Qrh3mIycAfPrjj0Rgxz8nRQbBLDbLF1QGPimt0zP69ByJ3opLujVVi5ixwgwza9S
+eGffKwGdyb9uFcB9MVnC997zfLvx/uNV44BwLnCH6Tp68Lynf+FpuvSX+Rsj4li3
+UiLoVxEIbBZ/5Bn3ygc7aW4fM4bR4hKjWwJR0Hh1y/kt6A7dEAypVKBfSqAtAu2A
+zYAq3USsbtq1X1FaGEsmvcJY8IGa+aLTArq7dkhXzcv7K3EVdqOawS1zS/ARuG+B
+k6kct3zzyj1EitiTvdMAkGOoyk16qKVzUcbFRVC1HsZtxYj6OxU4Eazvh0LjvZ0A
++eft/XO/ZmN6vyRaF1/10z+uHPfj1FLMpS8Zn4SN6x7Qtsx3iLL1n9cKBDFRXCqD
+HDaxLVC3N4zAI2hMMmZid+fbTuhsqYbSX2cAAwUL/j/H4/9Ml7PUUCXoozX2V4K+
+gi6WEYmY+pXN/we9NuFulW4aURo7jK4wRYBu0BS3K9e8f8WUMAV5V6ShPWHXcobt
+iuSjLYJwdBJkgHbnKFWPZUozJ3Ftyp0Lh1M5bN7/ECofAxLHbRpCVrcOP2LC7vAU
+AeMgdiFDqEiLCnr/aGvqUOxbGO6Isi4jvaM/ZUfGjGe/Z6yVoqm6wEsNM7+9cFGQ
+QR1lRPeCPKcLeasCdbM5EIt1aLFNijZigWuDRLIgG5PuzA8Kpdk/u/UuCUeUFwJN
+ym8MEv2JJDiWHmb8IcgFMp40VenUs0fte0LWwrMjWVPpLsHKmkraRjQ1UtarRhT0
+ANYilGjZWCnCb11xGKhlM7r5IkLGY/L/Eh4vjLgg9T5rGwOF8p1jSgx9mA8SpHV0
+O0BoKNX1ApWEHayTLcyayCnTYbY/e4axnSKodixAI/NghOnJHqGr4LeZeKk/Q0mm
+GlljzFv3EAdoru4DVowWGFBmrwBy7o+GLgHs6K/+yIh4BBgRCAAgAhsMFiEECRxE
+zpz7w/9+x6ZNyKEKfXgnPhAFAlxVr64ACgkQyKEKfXgnPhCETQEApruWUqCwfibQ
+vyI/OZohPzljlvIoioj3rFjYNpufQD8A/RTaYtnPiEvsPynEZCj9zTV/SuHiKbHS
+v5BhpoOOm+jM
+=PnGk
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/ed25519.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/ed25519.asc
new file mode 100644
index 0000000000..636a5a95f3
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/ed25519.asc
@@ -0,0 +1,9 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEWsN6MBYJKwYBBAHaRw8BAQdAAS+nkv9BdVi0JX7g6d+O201bdKhdowbielOo
+ugCpCfi0CWVjYy0yNTUxOYiUBBMWCAA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgID
+AQIeAwIXgBYhBCH8aCdKrjtd45pCd8x4YniYGwcoBQJcVa/NAAoJEMx4YniYGwco
+lFAA/jMt3RUUb5xt63JW6HFcrYq0RrDAcYMsXAY73iZpPsEcAQDmKbH21LkwoClU
+9RrUJSYZnMla/pQdgOxd7/PjRCpbCg==
+=miZp
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/faked.key b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/faked.key
new file mode 100644
index 0000000000..405afad427
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/faked.key
@@ -0,0 +1,23 @@
+Meta-Description: Example from GPG file 'keyformat.txt'.
+Description: Key to sign all GnuPG released tarballs.
+ The key is actually stored on a smart card.
+Use-for-ssh: yes
+OpenSSH-cert: long base64 encoded string wrapped so that this
+ key file can be easily edited with a standard editor.
+Token: D2760001240102000005000011730000 OPENPGP.1
+Token: FF020001008A77C1 PIV.9C
+Key: (shadowed-private-key
+ (rsa
+ (n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900
+ 2961D8AEA153424DC851EF13B83AC64FBE365C59DC1BD3E83017C90D4365B4
+ 83E02859FC13DB5842A00E969480DB96CE6F7D1C03600392B8E08EF0C01FC7
+ 19F9F9086B25AD39B4F1C2A2DF3E2BE317110CFFF21D4A11455508FE407997
+ 601260816C8422297C0637BB291C3A079B9CB38A92CE9E551F80AA0EBF4F0E
+ 72C3F250461E4D31F23A7087857FC8438324A013634563D34EFDDCBF2EA80D
+ F9662C9CCD4BEF2522D8BDFED24CEF78DC6B309317407EAC576D889F88ADA0
+ 8C4FFB480981FB68C5C6CA27503381D41018E6CDC52AAAE46B166BDC10637A
+ E186A02BA2497FDC5D1221#)
+ (e #00010001#)
+ (shadowed t1-v1
+ (#D2760001240102000005000011730000# OPENPGP.1)
+ ))) \ No newline at end of file
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp256.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp256.asc
new file mode 100644
index 0000000000..fd1509e9ba
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp256.asc
@@ -0,0 +1,14 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mFIEWsOCNRMIKoZIzj0DAQcCAwQS5G6mn5dhamZ6678SXE1azavqf8BItWO9Qv8V
+dS1vEEoD14urr5OQKTLuHhDRjvSQdaxRtkf0sI51T7230sT3tAhlY2MtcDI1NoiU
+BBMTCAA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgIDAQIeAwIXgBYhBLVP3ru2c0I6
+XQqlRCNnTyGyRBUnBQJcVa/nAAoJECNnTyGyRBUn1ycA+wVg9sEfHDBaGtLqlUSB
+WdGKURrHN7CJe2UTz1/7oQCBAQDDi4RQyLHs+TfOrBNSbLEswCu1oEh8VmHt/SN7
++mqNLbhWBFrDgjUSCCqGSM49AwEHAgMELDOArLIG85ABQu1IwgQMpiIuUwj+N7ib
+gGenTRck5dkBpX48eK3lbjovXn4YkBneA7z14iez3+Sdg6UFAMFV2QMBCAeIeAQY
+EwgAIAIbDBYhBLVP3ru2c0I6XQqlRCNnTyGyRBUnBQJcVa/vAAoJECNnTyGyRBUn
+ZKoBAJ64gv3w27nFBERvIsRqufvR6xcimqS7Gif+WehBU+P5AQC5bqoISh0oSQid
+adI84f60RuOaozpjvR3B1bPZiR6u7w==
+=H2xn
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp384.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp384.asc
new file mode 100644
index 0000000000..b2b59959e6
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp384.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mG8EWsOCnBMFK4EEACIDAwTRTCLBHlq6SUTiXZfWR0vbUh/0VAlrePaqHVIE4LEK
+0UBhPCIQOGuGL4JIufc8UzBWhiMLJ0z7KRjBWufsMZR2+rqj+unOK2lLu7sc9or8
+X6B74hhP3Ili24PgGFAeAG+0CGVjYy1wMzg0iLQEExMJADwCGwMFCwkIBwIDIgIB
+BhUKCQgLAgQWAgMBAh4DAheAFiEEqyXLoELdkkw6zD7TJCo6peqF9EoFAlxVsBEA
+CgkQJCo6peqF9EooJQF7BPZelriXwZ/kJzaamImHBddkLFc7d2WbuSfDxEZQ+Mfw
+BAP3+QYUaFtfeqApjY69AX4w6LhTUgI2kl4O0Vc7ZOlqZBlwAc8CMV08TTfOEio2
+b51SItvhLdDrFRJ2K4jiO+a4cwRaw4KcEgUrgQQAIgMDBORWqhYflSrYzF04SK8q
+8Om+DYTvwRtUlr3Aoq44+gm5yBcmJmgT3TKrp/bx5Jg/zwzIASFn0agbxkqKpQqH
+sHeelWsSBROQzy98HXdCp3nVmghI2aDk8zdD6AV4m7c2ewMBCQmImAQYEwkAIAIb
+DBYhBKsly6BC3ZJMOsw+0yQqOqXqhfRKBQJcVbAZAAoJECQqOqXqhfRKgAIBf3Wk
+TsqUA1JXkPGetA9sjHglIICN+DZY5k+PwTJUxaW2zrkiPJ3BYEnKbmmBLzA7BgGA
+4RYatyl2WOUYh/poRLgu7JpE4oRqdmNA+QOpCILMId1AeXfj4W01RKFWaKeH+3Yy
+=2H/0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp521.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp521.asc
new file mode 100644
index 0000000000..db18f81a29
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp521.asc
@@ -0,0 +1,19 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mJMEWsODGxMFK4EEACMEIwQA8OZCJ8Iv4Qr2oRr0kqez0nPSW/vNxYBZRpCJ9ab8
+kVaRhW7+5vEsecm5XugZSePbAmERmk3oEiSgu35r6aovowcAGrOBfBm9fyIVqaoX
+veTS3yRHH6NEf044+pC+uBaaFukkrDmVTecdRvYr3Yrdc5ifyOve053znlpQ6a4n
+9bh4GGy0CGVjYy1wNTIxiNYEExMKADwCGwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMB
+Ah4DAheAFiEET7Of9vpIV6S9fvW0IJLKgyQmO2oFAlxVsCkACgkQIJLKgyQmO2oK
+DwIGO72zo6otVkbHfeI9hWx/8FAOXh4MT4YtDicF/sj8QbHzdbEBHcLCByLYAnph
+8VVoCxpPcBLmNSHbNyreoksjEE0CB10P5kPrd/bYkdNx/HTu+1i8f7a448+Nr2rF
+PwdI9tOsghkT41qobZavjjnBlT/xv5DqXldJkEfaeiJxPHOKvMhWuJcEWsODGxIF
+K4EEACMEIwQBAY7ZCAjks1MWWxibg/EVaz5t6iEKJTwu8mGGKWdPZAQRKKNtNpf0
+pZAMV3I8ue/WQMsYKRYv5AGq1PnjV19DmLsA0aGw4MDM260coctkcn/2MAJQMC9+
+3Z+BJS3hqzwDuZ+LS13r0RLpgnt3ks+3ucG4II38ZZ1lTwKoIc+w/OuhsOIDAQoJ
+iLsEGBMKACACGwwWIQRPs5/2+khXpL1+9bQgksqDJCY7agUCXFWwLgAKCRAgksqD
+JCY7ahqbAgkBiXYtiBlp5dmSYnbc4JoIYWcxTBQ+/dGHyU6ZEfC5VQz2mrdJetK1
+bIID0rFSsd24/8IzAqM3L+nY9h9bULWroroCBjTohh0j2EbW+hFOrRqL01osnlY+
+1/G8e44blB5JqsPI9FqOZOUj6IzsUuV1N9gJbm1RHu/hSpm52d6rX4nOTbqt
+=3jnl
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/rsa.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/rsa.asc
new file mode 100644
index 0000000000..e74df7a2a9
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/rsa.asc
@@ -0,0 +1,40 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQGNBFrDgZ4BDACxg83nNkYvIAz6Nk4Np4BscWDrrL3tyiiQSz1yfCotxO8zUtVl
+JorSlqRurNdAYU2XJLakMqpQHE7VVIMI/WdXCC8CrQbULOBxIf/LGiQf0VDo8ukg
+iFFd5vUeMRRILWKnMc/GCmFkFOUHd1Y60h96oe5f/d286fRZQnCO8PGS8CgB1mDJ
+GBY8U1DCCkt6g9O6bfFkfcwetr1+kB2cBIY7uDzN1Sm8dz2VrkqPqtqt/F02giRN
+hD2GxX9HVYCkCKEE9DFsB+MDKR9z5lXrI3SAsL/3htXK/ukWgBe4DIFjTXbrzgP+
+nuWb4s1NxwSD7yjlnqVD7mZTGAwMsQbCjhwIeY4t5onsCJ4/40zm1jIZ5TJsfQ/b
+5gA7iu9kMKzxO0bPI2loJEB/K9aH7qsyQHgtt7G3+G8JrixLBUobtreY9V2QvhtC
+Q4OAHRO5nIKYIazDuKv9bxautZ0WuLOk/qmWMMoqFAn5bzQeVTTBD6kvr6l8/+MZ
+zaij9YPeUbeKJuMAEQEAAbQHcnNhLXJzYYkB0gQTAQIAPAIbAwULCQgHAgMiAgEG
+FQoJCAsCBBYCAwECHgMCF4AWIQRrwEpaPds1dmuaQNgvuReRGImOiwUCXFWwhwAK
+CRAvuReRGImOi1NiC/0RPjbQTWMw4/GxIMkRb9kmmsSUe7kCgqBCqZTxcI7rxZdn
+JFDbP5c6DAS/11bRQJ9OsoUjbDx0d81UuBlXB/mNsb9nCcXOrqAUHRqgWoNSDk4g
+9Oa1Kx77OM9BvRJbJGch2YW5Wcch5vcqQNu+6x3VGt7ipljYEJSQ6Dre+dgxYjXK
+60x63/ulFk2XImPQYjQ8VHbW/HDg/+DLf++phjVy9l58U1sUKSSdO8uuYoW6dBv2
+xRg18Sn3DWOU+mrkV8Ld95+NRRE1cSHTQv5hu4ELqrV+YdGNmv9DgQAMJOl3xy8i
+vOz4cpKaOBasm423wr5Y56nOTzLFN+dxnYR8tbqswLkCldRY6fvL1NsS77rj0yZp
+pecCyi0E6RAcmSiZJqpnOpcuI76AkZuWSDEP3Y3x5QBf5fu2uiQQsPXYN8ie6xcC
+zeYtXsHyNxF0oBh2c26po8fo4E4T70RSO8Oqs1XzXnjIIle8pKU9M5U5ISbWS3Hj
+vtOn5ZrLC5KYnVRna3G5AY0EWsOBngEMALmXpJoPC1m4THYrfHibtt2/OwAlDm20
+3xn+Klw69bkeXdc71wsLOAHVL3+7gXpip+IYmN7CBIyqlOCtsluu+gwP3MczPJZX
+vk1uXMMfLKiXl9Kodx/Fqq0Y26Tqse5PPMlagPStIvKyT0WTa3RCD28uVklapLuy
+1w1k4G5hIDPt6uKyxXq/HzneRSGWafmqoCWOmXQZzfOMG249bMXNOcPMJhOejPRS
+jREnnntbpvZ8DU/38+JFtqCUkPwuqYQkvGCKReSBifMiG3XAhHWOGzXPzdW1XdAi
+aA+NQP/kMUs45jS3hDdp4EObllYRBsQwtFpKPMNmwaVuOmVlbrXTP0YsDYGndkE6
+5nJ46/2xPhl8+nIgDLg3SBBzQdOiPOGtHYjs0bRKdwXTeAq4fDq0vCQXMJF2fwAQ
+LEYWs7kabKhcPpWzTtoCG78WzR3TgldEPhPjE0offvVQO56x1XDqMBctoiDWkWkS
+bdi03GhbFdK5A7uYBTJYEoo61Yp/2/MjyQARAQABiQG2BBgBAgAgAhsMFiEEa8BK
+Wj3bNXZrmkDYL7kXkRiJjosFAlxVsI8ACgkQL7kXkRiJjoumjwv/c6a9G1bi3sh7
+wRFhpsrFUoFfEBDI4eyI/haWhCIfI8n7p3lCSIy8lmf9yvUs75d5M3EQW08NQjIs
+/o8FcFoUBnKQv2whWSHTpx/BkuhcNVY+NIwyBKomU0WkFSm3+80ix0uh97KSlRlW
+Q5nMVExNxZ4mRFAhDQrJ2/pZ2DaddeO+4uZ7Twquaix+PMxpNKvkj2+757L23YjF
+QmHdk6E8burofpSCfBTB84eUSDvzs6Eb/34/KlbBZhKMYdffMDCSAZIMfIav6YVJ
+UDzT40kmS0vRW6bDIetSbpBM+GD1cSq0wKdlt+Giur9ZiaiyHIEqbPgr6WgdND25
+Vx/i23Ik2o8wMb0Ub8cKD9wjdGAk+Rt2r7d2RzyO/R3ThKbUOGkQX6acAAZAjhPs
+UGxt1dDojmQ3nF4l2hZ9PcsyD3pz226wUUPT4JA1eE6tdoVjzY2J7EhfNaVcQQlb
+bQJQ+BQcO4oP1mPRCx1GiSmB+jRNQ4npxVJxLO/j7T27CrSZbhT7
+=aibx
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/secp256k1.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/secp256k1.asc
new file mode 100644
index 0000000000..837f8a867e
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/secp256k1.asc
@@ -0,0 +1,14 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mE8EWsOE8xMFK4EEAAoCAwQVHqFZWqedXUIkNFs82PsQB3bsCDhrL/73xZca3+vo
+kB4T7jHcACThuMZYuUqUo9NzNTJioluOvZG+UdYXPdfdtAplY2MtcDI1NmsxiJQE
+ExMIADwCGwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4DAheAFiEEgfdytX1Ov+cA
+CmYjPqW7b5aSwaAFAlxVsQ0ACgkQPqW7b5aSwaD2tQD/R4d15NBuSJ6IB1brH0E9
+nEWkqo892PaAY5akdCO/i9EBAMsjE5NPxBnCs03c+VHFU200k27ixdrWpUa+HZEI
+A5wSuFMEWsOE8xIFK4EEAAoCAwSUWwe7CaaOYRANiKet2evLiOumefIHuvRpyOSK
+hyRdclIWpBUCAWEnmalkEL/8cEM5fjtILtCOKXqCOBsPv45HAwEIB4h4BBgTCAAg
+AhsMFiEEgfdytX1Ov+cACmYjPqW7b5aSwaAFAlxVsRUACgkQPqW7b5aSwaCETgD/
+YXzCMYMbPGAU2oTitjAno8hDWmgTeaFWeCmqf6l9mP8BAKvpewWeFGZfWGAQcWPi
+E+jv7vadvEt1yMA8rmT041F5
+=mDCI
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/x25519.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/x25519.asc
new file mode 100644
index 0000000000..d531d7a8ba
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/x25519.asc
@@ -0,0 +1,13 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEW8SVGhYJKwYBBAHaRw8BAQdA9SMZ2uw0YugMFcl5TpEZeBRAGniEk9a42XNs
+7QA4Tky0DGVkZHNhLXgyNTUxOYiQBBMWCAA4FiEETJc4pvK+Thp5bJt7lBgioPwb
+MKUFAlvElRoCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQlBgioPwbMKUi
+1wEAgMq3X7o17OJBPfY3He/exDR6LhWwAAXrVQR/WdRiHkEBALd1Mj0BlZZLoKTr
+uJ4MD5CYZLicXTRwOv6e52F/DHwJuDgEW8SVGhIKKwYBBAGXVQEFAQEHQA0Lh2mG
+lB1O4xDYgztm/aX7+8AdHEGaMsCF1RQ6wVUeAwEIB4h4BBgWCAAgFiEETJc4pvK+
+Thp5bJt7lBgioPwbMKUFAlvElRoCGwwACgkQlBgioPwbMKXmlQD+KxVg2dGL8lRW
+rQajwzmuwMrJX1lvJylg5Ozk6SGrBeABANZrdt8bmArEqeRVxFO2F4P7btyIpf1w
+5aNpqqtvkRcB
+=EYfV
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocatorTest.java b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocatorTest.java
index 744620163d..5f43378705 100644
--- a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocatorTest.java
+++ b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocatorTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2019, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -53,7 +53,7 @@ public class BouncyCastleGpgKeyLocatorTest {
assertFalse(match(USER_ID, "<heinrichh>"));
assertFalse(match(USER_ID, "<uni-duesseldorf>"));
assertFalse(match(USER_ID, "<h@u>"));
- assertFalse(match(USER_ID, "<HeinrichH@uni-duesseldorf.de>"));
+ assertTrue(match(USER_ID, "<HeinrichH@uni-duesseldorf.de>"));
assertFalse(match(USER_ID.substring(0, USER_ID.length() - 1),
"<heinrichh@uni-duesseldorf.de>"));
assertFalse(match("", "<>"));
@@ -72,8 +72,8 @@ public class BouncyCastleGpgKeyLocatorTest {
assertFalse(match(USER_ID, "@ "));
assertFalse(match(USER_ID, "@"));
assertFalse(match(USER_ID, "@Heine"));
- assertFalse(match(USER_ID, "@HeinrichH"));
- assertFalse(match(USER_ID, "@Heinrich"));
+ assertTrue(match(USER_ID, "@HeinrichH"));
+ assertTrue(match(USER_ID, "@Heinrich"));
assertFalse(match("", "@"));
assertFalse(match("", "@h"));
}
@@ -110,6 +110,7 @@ public class BouncyCastleGpgKeyLocatorTest {
public void testExplicitFingerprint() throws Exception {
assertFalse(match("John Fade <j.fade@example.com>", "0xfade"));
assertFalse(match("John Fade <0xfade@example.com>", "0xfade"));
+ assertFalse(match("John Fade <0xfade@example.com>", "0xFADE"));
assertFalse(match("", "0xfade"));
}
@@ -128,7 +129,7 @@ public class BouncyCastleGpgKeyLocatorTest {
assertTrue(match("John Fade <0xfade@example.com>", "*0xfade"));
assertTrue(match("John Fade <0xfade@example.com>", "*0xFADE"));
assertTrue(match("John Fade <0xfade@example.com>", "@0xfade"));
- assertFalse(match("John Fade <0xfade@example.com>", "@0xFADE"));
+ assertTrue(match("John Fade <0xfade@example.com>", "@0xFADE"));
assertFalse(match("", "0x"));
}
}
diff --git a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip25519Test.java b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip25519Test.java
new file mode 100644
index 0000000000..e30080258f
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip25519Test.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import static org.junit.Assert.assertEquals;
+
+import java.math.BigInteger;
+import java.util.Locale;
+
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.util.encoders.Hex;
+import org.eclipse.jgit.util.sha1.SHA1;
+import org.junit.Test;
+
+public class KeyGrip25519Test {
+
+ interface Hash {
+ byte[] hash(SHA1 sha, BigInteger q) throws PGPException;
+ }
+
+ private void assertKeyGrip(String key, String expectedKeyGrip, Hash hash)
+ throws Exception {
+ SHA1 grip = SHA1.newInstance();
+ grip.setDetectCollision(false);
+ BigInteger pk = new BigInteger(key, 16);
+ byte[] keyGrip = hash.hash(grip, pk);
+ assertEquals("Keygrip should match", expectedKeyGrip,
+ Hex.toHexString(keyGrip).toUpperCase(Locale.ROOT));
+ }
+
+ @Test
+ public void testCompressed() throws Exception {
+ assertKeyGrip("40"
+ + "773E72848C1FD5F9652B29E2E7AF79571A04990E96F2016BF4E0EC1890C2B7DB",
+ "9DB6C64A38830F4960701789475520BE8C821F47",
+ KeyGrip::hashEd25519);
+ }
+
+ @Test
+ public void testCompressedNoPrefix() throws Exception {
+ assertKeyGrip(
+ "773E72848C1FD5F9652B29E2E7AF79571A04990E96F2016BF4E0EC1890C2B7DB",
+ "9DB6C64A38830F4960701789475520BE8C821F47",
+ KeyGrip::hashEd25519);
+ }
+
+ @Test
+ public void testCurve25519() throws Exception {
+ assertKeyGrip("40"
+ + "918C1733127F6BF2646FAE3D081A18AE77111C903B906310B077505EFFF12740",
+ "0F89A565D3EA187CE839332398F5D480677DF49C",
+ KeyGrip::hashCurve25519);
+ }
+}
diff --git a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGripTest.java b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGripTest.java
new file mode 100644
index 0000000000..a4aaf40d9b
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGripTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Security;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.function.Consumer;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
+import org.bouncycastle.util.encoders.Hex;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class KeyGripTest {
+
+ @BeforeClass
+ public static void ensureBC() {
+ if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ }
+
+ protected static class TestData {
+
+ String filename;
+
+ String[] expectedKeyGrips;
+
+ TestData(String filename, String... keyGrips) {
+ this.filename = filename;
+ this.expectedKeyGrips = keyGrips;
+ }
+
+ @Override
+ public String toString() {
+ return filename;
+ }
+ }
+
+ @Parameters(name = "{0}")
+ public static TestData[] initTestData() {
+ return new TestData[] {
+ new TestData("rsa.asc",
+ "D148210FAF36468055B83D0F5A6DEB83FBC8E864",
+ "A5E4CD2CBBE44A16E4D6EC05C2E3C3A599DC763C"),
+ new TestData("dsa-elgamal.asc",
+ "552286BEB2999F0A9E26A50385B90D9724001187",
+ "CED7034A8EB5F4CE90DF99147EC33D86FCD3296C"),
+ new TestData("brainpool256.asc",
+ "A01BAA22A72F09A0FF0A1D4CBCE70844DD52DDD7",
+ "C1678B7DE5F144C93B89468D5F9764ACE182ED36"),
+ new TestData("brainpool384.asc",
+ "2F25DB025DEBF3EA2715350209B985829B04F50A",
+ "B6BD8B81F75AF914163D97DF8DE8F6FC64C283F8"),
+ new TestData("brainpool512.asc",
+ "5A484F56AB4B8B6583B6365034999F6543FAE1AE",
+ "9133E4A7E8FC8515518DF444C3F2F247EEBBADEC"),
+ new TestData("nistp256.asc",
+ "FC81AECE90BCE6E54D0D637D266109783AC8DAC0",
+ "A56DC8DB8355747A809037459B4258B8A743EAB5"),
+ new TestData("nistp384.asc",
+ "A1338230AED1C9C125663518470B49056C9D1733",
+ "797A83FE041FFE06A7F4B1D32C6F4AE0F6D87ADF"),
+ new TestData("nistp521.asc",
+ "D91B789603EC9138AA20342A2B6DC86C81B70F5D",
+ "FD048B2CA1919CB241DC8A2C7FA3E742EF343DCA"),
+ new TestData("secp256k1.asc",
+ "498B89C485489BA16B40755C0EBA580166393074",
+ "48FFED40D018747363BDEFFDD404D1F4870F8064"),
+ new TestData("ed25519.asc",
+ "940D97D75C306D737A59A98EAFF1272832CEDC0B"),
+ new TestData("x25519.asc",
+ "A77DC8173DA6BEE126F5BD6F5A14E01200B52FCE",
+ "636C983EDB558527BA82780B52CB5DAE011BE46B")
+ };
+ }
+
+ // Injected by JUnit
+ @Parameter
+ public TestData data;
+
+ private void readAsc(InputStream in, Consumer<PGPPublicKey> process)
+ throws IOException, PGPException {
+ PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
+ PGPUtil.getDecoderStream(in), new JcaKeyFingerprintCalculator());
+
+ Iterator<PGPPublicKeyRing> keyRings = pgpPub.getKeyRings();
+ while (keyRings.hasNext()) {
+ PGPPublicKeyRing keyRing = keyRings.next();
+
+ Iterator<PGPPublicKey> keys = keyRing.getPublicKeys();
+ while (keys.hasNext()) {
+ process.accept(keys.next());
+ }
+ }
+ }
+
+ @Test
+ public void testGrip() throws Exception {
+ try (InputStream in = this.getClass()
+ .getResourceAsStream(data.filename)) {
+ int index[] = { 0 };
+ readAsc(in, key -> {
+ byte[] keyGrip = null;
+ try {
+ keyGrip = KeyGrip.getKeyGrip(key);
+ } catch (PGPException e) {
+ throw new RuntimeException(e);
+ }
+ assertTrue("More keys than expected",
+ index[0] < data.expectedKeyGrips.length);
+ assertEquals("Wrong keygrip", data.expectedKeyGrips[index[0]++],
+ Hex.toHexString(keyGrip).toUpperCase(Locale.ROOT));
+ });
+ assertEquals("Missing keys", data.expectedKeyGrips.length,
+ index[0]);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeysTest.java b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeysTest.java
new file mode 100644
index 0000000000..5e5e303319
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeysTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Security;
+import java.util.Iterator;
+
+import javax.crypto.Cipher;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSecretKey;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
+import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SecretKeysTest {
+
+ @BeforeClass
+ public static void ensureBC() {
+ if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ }
+
+ private static volatile Boolean haveOCB;
+
+ private static boolean ocbAvailable() {
+ Boolean haveIt = haveOCB;
+ if (haveIt != null) {
+ return haveIt.booleanValue();
+ }
+ try {
+ Cipher c = Cipher.getInstance("AES/OCB/NoPadding"); //$NON-NLS-1$
+ if (c == null) {
+ haveOCB = Boolean.FALSE;
+ return false;
+ }
+ } catch (NoClassDefFoundError | Exception e) {
+ haveOCB = Boolean.FALSE;
+ return false;
+ }
+ haveOCB = Boolean.TRUE;
+ return true;
+ }
+
+ private static class TestData {
+
+ final String name;
+
+ final boolean encrypted;
+
+ final boolean keyValue;
+
+ TestData(String name, boolean encrypted, boolean keyValue) {
+ this.name = name;
+ this.encrypted = encrypted;
+ this.keyValue = keyValue;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ @Parameters(name = "{0}")
+ public static TestData[] initTestData() {
+ return new TestData[] {
+ new TestData("AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11", false, false),
+ new TestData("2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A", false, true),
+ new TestData("66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9", true, true),
+ new TestData("F727FAB884DA3BD402B6E0F5472E108D21033124", true, true),
+ new TestData("faked", false, true) };
+ }
+
+ private static byte[] readTestKey(String filename) throws Exception {
+ try (InputStream in = new BufferedInputStream(
+ SecretKeysTest.class.getResourceAsStream(filename))) {
+ return SecretKeys.keyFromNameValueFormat(in);
+ }
+ }
+
+ private static PGPPublicKey readAsc(InputStream in)
+ throws IOException, PGPException {
+ PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
+ PGPUtil.getDecoderStream(in), new JcaKeyFingerprintCalculator());
+
+ Iterator<PGPPublicKeyRing> keyRings = pgpPub.getKeyRings();
+ while (keyRings.hasNext()) {
+ PGPPublicKeyRing keyRing = keyRings.next();
+
+ Iterator<PGPPublicKey> keys = keyRing.getPublicKeys();
+ if (keys.hasNext()) {
+ return keys.next();
+ }
+ }
+ return null;
+ }
+
+ // Injected by JUnit
+ @Parameter
+ public TestData data;
+
+ @Test
+ public void testKeyRead() throws Exception {
+ if (data.keyValue) {
+ byte[] bytes = readTestKey(data.name + ".key");
+ assertEquals('(', bytes[0]);
+ assertEquals(')', bytes[bytes.length - 1]);
+ }
+ try (InputStream pubIn = this.getClass()
+ .getResourceAsStream(data.name + ".asc")) {
+ if (pubIn != null) {
+ PGPPublicKey publicKey = readAsc(pubIn);
+ // Do a full test trying to load the secret key.
+ PGPDigestCalculatorProvider calculatorProvider = new JcaPGPDigestCalculatorProviderBuilder()
+ .build();
+ try (InputStream in = new BufferedInputStream(this.getClass()
+ .getResourceAsStream(data.name + ".key"))) {
+ PGPSecretKey secretKey = SecretKeys.readSecretKey(in,
+ calculatorProvider,
+ data.encrypted ? () -> "nonsense".toCharArray()
+ : null,
+ publicKey);
+ assertNotNull(secretKey);
+ } catch (PGPException e) {
+ // Currently we may not be able to load OCB-encrypted keys.
+ assertTrue(e.getMessage().contains("OCB"));
+ assertTrue(data.encrypted);
+ assertFalse(ocbAvailable());
+ }
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
index b59a850ea3..48aad320dc 100644
--- a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
@@ -8,22 +8,30 @@ Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-Version: 6.0.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.bouncycastle.bcpg;version="[1.65.0,2.0.0)",
+Import-Package: org.bouncycastle.asn1;version="[1.65.0,2.0.0)",
+ org.bouncycastle.asn1.cryptlib;version="[1.65.0,2.0.0)",
+ org.bouncycastle.asn1.x9;version="[1.65.0,2.0.0)",
+ org.bouncycastle.bcpg;version="[1.65.0,2.0.0)",
+ org.bouncycastle.bcpg.sig;version="[1.65.0,2.0.0)",
+ org.bouncycastle.crypto.ec;version="[1.65.0,2.0.0)",
org.bouncycastle.gpg;version="[1.65.0,2.0.0)",
org.bouncycastle.gpg.keybox;version="[1.65.0,2.0.0)",
org.bouncycastle.gpg.keybox.jcajce;version="[1.65.0,2.0.0)",
+ org.bouncycastle.jcajce.interfaces;version="[1.65.0,2.0.0)",
+ org.bouncycastle.jcajce.util;version="[1.65.0,2.0.0)",
org.bouncycastle.jce.provider;version="[1.65.0,2.0.0)",
+ org.bouncycastle.math.ec;version="[1.65.0,2.0.0)",
+ org.bouncycastle.math.field;version="[1.65.0,2.0.0)",
org.bouncycastle.openpgp;version="[1.65.0,2.0.0)",
+ org.bouncycastle.openpgp.jcajce;version="[1.65.0,2.0.0)",
org.bouncycastle.openpgp.operator;version="[1.65.0,2.0.0)",
org.bouncycastle.openpgp.operator.jcajce;version="[1.65.0,2.0.0)",
+ org.bouncycastle.util;version="[1.65.0,2.0.0)",
org.bouncycastle.util.encoders;version="[1.65.0,2.0.0)",
+ org.bouncycastle.util.io;version="[1.65.0,2.0.0)",
org.eclipse.jgit.annotations;version="[6.0.0,6.1.0)",
org.eclipse.jgit.api.errors;version="[6.0.0,6.1.0)",
- org.eclipse.jgit.errors;version="[6.0.0,6.1.0)",
- org.eclipse.jgit.lib;version="[6.0.0,6.1.0)",
- org.eclipse.jgit.nls;version="[6.0.0,6.1.0)",
- org.eclipse.jgit.transport;version="[6.0.0,6.1.0)",
- org.eclipse.jgit.util;version="[6.0.0,6.1.0)",
org.slf4j;version="[1.7.0,2.0.0)"
-Export-Package: org.eclipse.jgit.gpg.bc.internal;version="6.0.0";
- x-friends:="org.eclipse.jgit.gpg.bc.test"
+Export-Package: org.eclipse.jgit.gpg.bc;version="6.0.0",
+ org.eclipse.jgit.gpg.bc.internal;version="6.0.0";x-friends:="org.eclipse.jgit.gpg.bc.test",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="6.0.0";x-friends:="org.eclipse.jgit.gpg.bc.test"
diff --git a/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
index 9e0d921ea2..58bd8ea041 100644
--- a/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
@@ -5,3 +5,4 @@ Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.source
Bundle-Vendor: Eclipse.org - JGit
Bundle-Version: 6.0.0.qualifier
Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="6.0.0.qualifier";roots="."
+
diff --git a/org.eclipse.jgit.gpg.bc/about.html b/org.eclipse.jgit.gpg.bc/about.html
index f971af18d0..fc527d5a3a 100644
--- a/org.eclipse.jgit.gpg.bc/about.html
+++ b/org.eclipse.jgit.gpg.bc/about.html
@@ -11,7 +11,7 @@
margin: 0.25in 0.5in 0.25in 0.5in;
tab-interval: 0.5in;
}
- p {
+ p {
margin-left: auto;
margin-top: 0.5em;
margin-bottom: 0.5em;
@@ -36,60 +36,53 @@
<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
<p>All rights reserved.</p>
-<p>Redistribution and use in source and binary forms, with or without modification,
+<p>Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
-<ul><li>Redistributions of source code must retain the above copyright notice,
+<ul><li>Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer. </li>
-<li>Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
+<li>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. </li>
-<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
- contributors may be used to endorse or promote products derived from
+<li>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. </li></ul>
</p>
-<p>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
+<p>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.</p>
<hr>
-<p><b>SHA-1 UbcCheck - MIT</b></p>
+<p><b>org.eclipse.jgit.gpg.bc.internal.keys.SExprParser - MIT</b></p>
-<p>Copyright (c) 2017:</p>
-<div class="ubc-name">
-Marc Stevens
-Cryptology Group
-Centrum Wiskunde & Informatica
-P.O. Box 94079, 1090 GB Amsterdam, Netherlands
-marc@marc-stevens.nl
-</div>
-<div class="ubc-name">
-Dan Shumow
-Microsoft Research
-danshu@microsoft.com
-</div>
-<p>Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+<p>Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc.
+(<a href="https://www.bouncycastle.org">https://www.bouncycastle.org</a>)</p>
+
+<p>
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+and associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+</p>
+<p>
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+</p>
+<p>
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
</p>
-<ul><li>The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.</li></ul>
-<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.</p>
</body>
diff --git a/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSignatureVerifierFactory b/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSignatureVerifierFactory
new file mode 100644
index 0000000000..17ab30fba7
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSignatureVerifierFactory
@@ -0,0 +1 @@
+org.eclipse.jgit.gpg.bc.internal.BouncyCastleGpgSignatureVerifierFactory \ No newline at end of file
diff --git a/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties b/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties
index 1441c63e8e..e4b1baba1f 100644
--- a/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties
+++ b/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties
@@ -1,11 +1,36 @@
+corrupt25519Key=Ed25519/Curve25519 public key has wrong length: {0}
credentialPassphrase=Passphrase
-gpgFailedToParseSecretKey=Failed to parse secret key file in directory: {0}. Is the entered passphrase correct?
+cryptCipherError=Cannot create cipher to decrypt: {0}
+cryptWrongDecryptedLength=Decrypted key has wrong length; expected {0} bytes, got only {1} bytes
+gpgFailedToParseSecretKey=Failed to parse secret key file {0}. Is the entered passphrase correct?
gpgNoCredentialsProvider=missing credentials provider
+gpgNoKeygrip=Cannot find key {0}: cannot determine key grip
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}
+gpgNoSuchAlgorithm=Cannot decrypt encrypted secret key: encryption algorithm {0} is not available
gpgNotASigningKey=Secret key ({0}) is not suitable for signing
gpgKeyInfo=GPG Key (fingerprint {0})
gpgSigningCancelled=Signing was cancelled
+nonSignatureError=Signature does not decode into a signature object
+secretKeyTooShort=Secret key file corrupt; only {0} bytes read
+sexprHexNotClosed=Hex number in s-expression not closed
+sexprHexOdd=Hex number in s-expression has an odd number of digits
+sexprStringInvalidEscape=Invalid escape {0} in s-expression
+sexprStringInvalidEscapeAtEnd=Invalid s-expression: quoted string ends with escape character
+sexprStringInvalidHexEscape=Invalid hex escape in s-expression
+sexprStringInvalidOctalEscape=Invalid octal escape in s-expression
+sexprStringNotClosed=String in s-expression not closed
+sexprUnhandled=Unhandled token {0} in s-expression
+signatureInconsistent=Inconsistent signature; key ID {0} does not match issuer fingerprint {1}
+signatureKeyLookupError=Error occurred while looking for public key
+signatureNoKeyInfo=No way to determine a public key from the signature
+signatureNoPublicKey=No public key found to verify the signature
+signatureParseError=Signature cannot be parsed
+signatureVerificationError=Signature verification failed
unableToSignCommitNoSecretKey=Unable to sign commit. Signing key not available.
+uncompressed25519Key=Cannot handle ed25519 public key with uncompressed data: {0}
+unknownCurve=Unknown curve {0}
+unknownCurveParameters=Curve {0} does not have a prime field
+unknownKeyType=Unknown key type {0} \ No newline at end of file
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/BouncyCastleGpgSignerFactory.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/BouncyCastleGpgSignerFactory.java
new file mode 100644
index 0000000000..fdd1a2b11a
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/BouncyCastleGpgSignerFactory.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc;
+
+import org.eclipse.jgit.gpg.bc.internal.BouncyCastleGpgSigner;
+import org.eclipse.jgit.lib.GpgSigner;
+
+/**
+ * Factory for creating a {@link GpgSigner} based on Bouncy Castle.
+ *
+ * @since 5.11
+ */
+public final class BouncyCastleGpgSignerFactory {
+
+ private BouncyCastleGpgSignerFactory() {
+ // No instantiation
+ }
+
+ /**
+ * Creates a new {@link GpgSigner}.
+ *
+ * @return the {@link GpgSigner}
+ */
+ public static GpgSigner create() {
+ return new BouncyCastleGpgSigner();
+ }
+}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java
index 1a00b0fc79..aedf8a5be5 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java
@@ -1,3 +1,12 @@
+/*
+ * Copyright (C) 2018, 2021 Salesforce and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
package org.eclipse.jgit.gpg.bc.internal;
import org.eclipse.jgit.nls.NLS;
@@ -18,16 +27,41 @@ public final class BCText extends TranslationBundle {
}
// @formatter:off
+ /***/ public String corrupt25519Key;
/***/ public String credentialPassphrase;
+ /***/ public String cryptCipherError;
+ /***/ public String cryptWrongDecryptedLength;
/***/ public String gpgFailedToParseSecretKey;
/***/ public String gpgNoCredentialsProvider;
+ /***/ public String gpgNoKeygrip;
/***/ public String gpgNoKeyring;
/***/ public String gpgNoKeyInLegacySecring;
/***/ public String gpgNoPublicKeyFound;
/***/ public String gpgNoSecretKeyForPublicKey;
+ /***/ public String gpgNoSuchAlgorithm;
/***/ public String gpgNotASigningKey;
/***/ public String gpgKeyInfo;
/***/ public String gpgSigningCancelled;
+ /***/ public String nonSignatureError;
+ /***/ public String secretKeyTooShort;
+ /***/ public String sexprHexNotClosed;
+ /***/ public String sexprHexOdd;
+ /***/ public String sexprStringInvalidEscape;
+ /***/ public String sexprStringInvalidEscapeAtEnd;
+ /***/ public String sexprStringInvalidHexEscape;
+ /***/ public String sexprStringInvalidOctalEscape;
+ /***/ public String sexprStringNotClosed;
+ /***/ public String sexprUnhandled;
+ /***/ public String signatureInconsistent;
+ /***/ public String signatureKeyLookupError;
+ /***/ public String signatureNoKeyInfo;
+ /***/ public String signatureNoPublicKey;
+ /***/ public String signatureParseError;
+ /***/ public String signatureVerificationError;
/***/ public String unableToSignCommitNoSecretKey;
+ /***/ public String uncompressed25519Key;
+ /***/ public String unknownCurve;
+ /***/ public String unknownCurveParameters;
+ /***/ public String unknownKeyType;
}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java
index eca45072bf..cf4d3d2340 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2020 Salesforce and others
+ * Copyright (C) 2018, 2021 Salesforce and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -14,25 +14,22 @@ import static java.nio.file.Files.newInputStream;
import java.io.BufferedInputStream;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.text.MessageFormat;
-import java.util.ArrayList;
import java.util.Iterator;
-import java.util.List;
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;
@@ -50,15 +47,15 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPUtil;
-import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
-import org.bouncycastle.openpgp.operator.jcajce.JcePBEProtectionRemoverFactory;
import org.bouncycastle.util.encoders.Hex;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.gpg.bc.internal.keys.KeyGrip;
+import org.eclipse.jgit.gpg.bc.internal.keys.SecretKeys;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.StringUtils;
import org.eclipse.jgit.util.SystemReader;
@@ -78,17 +75,10 @@ public class BouncyCastleGpgKeyLocator {
}
- /** Thrown if we try to read an encrypted private key without password. */
- private static class EncryptedPgpKeyException extends RuntimeException {
-
- private static final long serialVersionUID = 1L;
-
- }
-
private static final Logger log = LoggerFactory
.getLogger(BouncyCastleGpgKeyLocator.class);
- private static final Path GPG_DIRECTORY = findGpgDirectory();
+ static final Path GPG_DIRECTORY = findGpgDirectory();
private static final Path USER_KEYBOX_PATH = GPG_DIRECTORY
.resolve("pubring.kbx"); //$NON-NLS-1$
@@ -155,16 +145,13 @@ public class BouncyCastleGpgKeyLocator {
private PGPSecretKey attemptParseSecretKey(Path keyFile,
PGPDigestCalculatorProvider calculatorProvider,
- PBEProtectionRemoverFactory passphraseProvider,
- PGPPublicKey publicKey) {
+ SecretKeys.PassphraseSupplier passphraseSupplier,
+ PGPPublicKey publicKey)
+ throws IOException, PGPException, CanceledException,
+ UnsupportedCredentialItem, URISyntaxException {
try (InputStream in = newInputStream(keyFile)) {
- return new SExprParser(calculatorProvider).parseSecretKey(
- new BufferedInputStream(in), passphraseProvider, publicKey);
- } catch (IOException | PGPException | ClassCastException e) {
- if (log.isDebugEnabled())
- log.debug("Ignoring unreadable file '{}': {}", keyFile, //$NON-NLS-1$
- e.getMessage(), e);
- return null;
+ return SecretKeys.readSecretKey(in, calculatorProvider,
+ passphraseSupplier, publicKey);
}
}
@@ -219,33 +206,60 @@ public class BouncyCastleGpgKeyLocator {
int stop = toMatch.indexOf('>');
return begin >= 0 && end > begin + 1 && stop > 0
&& userId.substring(begin + 1, end)
- .equals(toMatch.substring(0, stop));
+ .equalsIgnoreCase(toMatch.substring(0, stop));
}
case '@': {
int begin = userId.indexOf('<');
int end = userId.indexOf('>', begin + 1);
return begin >= 0 && end > begin + 1
- && userId.substring(begin + 1, end).contains(toMatch);
+ && containsIgnoreCase(userId.substring(begin + 1, end),
+ toMatch);
}
default:
if (toMatch.trim().isEmpty()) {
return false;
}
- return userId.toLowerCase(Locale.ROOT)
- .contains(toMatch.toLowerCase(Locale.ROOT));
+ return containsIgnoreCase(userId, toMatch);
+ }
+ }
+
+ private static boolean containsIgnoreCase(String a, String b) {
+ int alength = a.length();
+ int blength = b.length();
+ for (int i = 0; i + blength <= alength; i++) {
+ if (a.regionMatches(true, i, b, 0, blength)) {
+ return true;
+ }
}
+ return false;
}
- private String toFingerprint(String keyId) {
+ private static String toFingerprint(String keyId) {
if (keyId.startsWith("0x")) { //$NON-NLS-1$
return keyId.substring(2);
}
return keyId;
}
- private PGPPublicKey findPublicKeyByKeyId(KeyBlob keyBlob)
+ static PGPPublicKey findPublicKey(String fingerprint, String keySpec)
+ throws IOException, PGPException {
+ PGPPublicKey result = findPublicKeyInPubring(USER_PGP_PUBRING_FILE,
+ fingerprint, keySpec);
+ if (result == null && exists(USER_KEYBOX_PATH)) {
+ try {
+ result = findPublicKeyInKeyBox(USER_KEYBOX_PATH, fingerprint,
+ keySpec);
+ } catch (NoSuchAlgorithmException | NoSuchProviderException
+ | IOException | NoOpenPgpKeyException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+ return result;
+ }
+
+ private static PGPPublicKey findPublicKeyByKeyId(KeyBlob keyBlob,
+ String keyId)
throws IOException {
- String keyId = toFingerprint(signingKey).toLowerCase(Locale.ROOT);
if (keyId.isEmpty()) {
return null;
}
@@ -259,10 +273,11 @@ public class BouncyCastleGpgKeyLocator {
return null;
}
- private PGPPublicKey findPublicKeyByUserId(KeyBlob keyBlob)
+ private static PGPPublicKey findPublicKeyByUserId(KeyBlob keyBlob,
+ String keySpec)
throws IOException {
for (UserID userID : keyBlob.getUserIds()) {
- if (containsSigningKey(userID.getUserIDAsString(), signingKey)) {
+ if (containsSigningKey(userID.getUserIDAsString(), keySpec)) {
return getSigningPublicKey(keyBlob);
}
}
@@ -274,6 +289,10 @@ public class BouncyCastleGpgKeyLocator {
*
* @param keyboxFile
* the KeyBox file
+ * @param keyId
+ * to look for, may be null
+ * @param keySpec
+ * to look for
* @return publicKey the public key (maybe <code>null</code>)
* @throws IOException
* in case of problems reading the file
@@ -282,19 +301,22 @@ public class BouncyCastleGpgKeyLocator {
* @throws NoOpenPgpKeyException
* if the file does not contain any OpenPGP key
*/
- private PGPPublicKey findPublicKeyInKeyBox(Path keyboxFile)
+ private static PGPPublicKey findPublicKeyInKeyBox(Path keyboxFile,
+ String keyId, String keySpec)
throws IOException, NoSuchAlgorithmException,
NoSuchProviderException, NoOpenPgpKeyException {
KeyBox keyBox = readKeyBoxFile(keyboxFile);
+ String id = keyId != null ? keyId
+ : toFingerprint(keySpec).toLowerCase(Locale.ROOT);
boolean hasOpenPgpKey = false;
for (KeyBlob keyBlob : keyBox.getKeyBlobs()) {
if (keyBlob.getType() == BlobType.OPEN_PGP_BLOB) {
hasOpenPgpKey = true;
- PGPPublicKey key = findPublicKeyByKeyId(keyBlob);
+ PGPPublicKey key = findPublicKeyByKeyId(keyBlob, id);
if (key != null) {
return key;
}
- key = findPublicKeyByUserId(keyBlob);
+ key = findPublicKeyByUserId(keyBlob, keySpec);
if (key != null) {
return key;
}
@@ -338,7 +360,8 @@ public class BouncyCastleGpgKeyLocator {
// pubring.gpg also try secring.gpg to find the secret key.
if (exists(USER_KEYBOX_PATH)) {
try {
- publicKey = findPublicKeyInKeyBox(USER_KEYBOX_PATH);
+ publicKey = findPublicKeyInKeyBox(USER_KEYBOX_PATH, null,
+ signingKey);
if (publicKey != null) {
key = findSecretKeyForKeyBoxPublicKey(publicKey,
USER_KEYBOX_PATH);
@@ -361,7 +384,8 @@ public class BouncyCastleGpgKeyLocator {
}
}
if (exists(USER_PGP_PUBRING_FILE)) {
- publicKey = findPublicKeyInPubring(USER_PGP_PUBRING_FILE);
+ publicKey = findPublicKeyInPubring(USER_PGP_PUBRING_FILE, null,
+ signingKey);
if (publicKey != null) {
// GPG < 2.1 may have both; the agent using the directory
// and gpg using secring.gpg. GPG >= 2.1 delegates all
@@ -433,67 +457,59 @@ public class BouncyCastleGpgKeyLocator {
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();
-
- try (Stream<Path> keyFiles = Files.walk(USER_SECRET_KEY_DIR)) {
- List<Path> allPaths = keyFiles.filter(Files::isRegularFile)
- .collect(Collectors.toCollection(ArrayList::new));
- if (allPaths.isEmpty()) {
- return null;
+ byte[] keyGrip = null;
+ try {
+ keyGrip = KeyGrip.getKeyGrip(publicKey);
+ } catch (PGPException e) {
+ throw new PGPException(
+ MessageFormat.format(BCText.get().gpgNoKeygrip,
+ Hex.toHexString(publicKey.getFingerprint())),
+ e);
+ }
+ String filename = Hex.toHexString(keyGrip).toUpperCase(Locale.ROOT)
+ + ".key"; //$NON-NLS-1$
+ Path keyFile = USER_SECRET_KEY_DIR.resolve(filename);
+ if (!Files.exists(keyFile)) {
+ return null;
+ }
+ boolean clearPrompt = false;
+ try {
+ PGPDigestCalculatorProvider calculatorProvider = new JcaPGPDigestCalculatorProviderBuilder()
+ .build();
+ clearPrompt = true;
+ PGPSecretKey secretKey = null;
+ try {
+ secretKey = attemptParseSecretKey(keyFile, calculatorProvider,
+ () -> passphrasePrompt.getPassphrase(
+ publicKey.getFingerprint(), userKeyboxPath),
+ publicKey);
+ } catch (PGPException e) {
+ throw new PGPException(MessageFormat.format(
+ BCText.get().gpgFailedToParseSecretKey,
+ keyFile.toAbsolutePath()), e);
}
- PBEProtectionRemoverFactory passphraseProvider = p -> {
- throw new EncryptedPgpKeyException();
- };
- for (int attempts = 0; attempts < 2; attempts++) {
- // Second pass will traverse only the encrypted keys with a real
- // passphrase provider.
- Iterator<Path> pathIterator = allPaths.iterator();
- while (pathIterator.hasNext()) {
- Path keyFile = pathIterator.next();
- try {
- PGPSecretKey secretKey = attemptParseSecretKey(keyFile,
- calculatorProvider, passphraseProvider,
- publicKey);
- pathIterator.remove();
- if (secretKey != null) {
- if (!secretKey.isSigningKey()) {
- throw new PGPException(MessageFormat.format(
- BCText.get().gpgNotASigningKey,
- signingKey));
- }
- return new BouncyCastleGpgKey(secretKey,
- userKeyboxPath);
- }
- } catch (EncryptedPgpKeyException e) {
- // Ignore; we'll try again.
- }
- }
- if (attempts > 0 || allPaths.isEmpty()) {
- break;
+ if (secretKey != null) {
+ if (!secretKey.isSigningKey()) {
+ throw new PGPException(MessageFormat.format(
+ BCText.get().gpgNotASigningKey, signingKey));
}
- // allPaths contains only the encrypted keys now.
- passphraseProvider = new JcePBEProtectionRemoverFactory(
- passphrasePrompt.getPassphrase(
- publicKey.getFingerprint(), userKeyboxPath));
+ clearPrompt = false;
+ return new BouncyCastleGpgKey(secretKey, userKeyboxPath);
}
-
- passphrasePrompt.clear();
return null;
} catch (RuntimeException e) {
- passphrasePrompt.clear();
throw e;
+ } catch (FileNotFoundException | NoSuchFileException e) {
+ clearPrompt = false;
+ return null;
} catch (IOException e) {
- passphrasePrompt.clear();
throw new PGPException(MessageFormat.format(
BCText.get().gpgFailedToParseSecretKey,
- USER_SECRET_KEY_DIR.toAbsolutePath()), e);
+ keyFile.toAbsolutePath()), e);
+ } finally {
+ if (clearPrompt) {
+ passphrasePrompt.clear();
+ }
}
}
@@ -551,6 +567,11 @@ public class BouncyCastleGpgKeyLocator {
* Return the first public key matching the key id ({@link #signingKey}.
*
* @param pubringFile
+ * to search
+ * @param keyId
+ * to look for, may be null
+ * @param keySpec
+ * to look for
*
* @return the PGP public key, or {@code null} if none found
* @throws IOException
@@ -558,14 +579,16 @@ public class BouncyCastleGpgKeyLocator {
* @throws PGPException
* on BouncyCastle errors
*/
- private PGPPublicKey findPublicKeyInPubring(Path pubringFile)
+ private static PGPPublicKey findPublicKeyInPubring(Path pubringFile,
+ String keyId, String keySpec)
throws IOException, PGPException {
try (InputStream in = newInputStream(pubringFile)) {
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
new BufferedInputStream(in),
new JcaKeyFingerprintCalculator());
- String keyId = toFingerprint(signingKey).toLowerCase(Locale.ROOT);
+ String id = keyId != null ? keyId
+ : toFingerprint(keySpec).toLowerCase(Locale.ROOT);
Iterator<PGPPublicKeyRing> keyrings = pgpPub.getKeyRings();
while (keyrings.hasNext()) {
PGPPublicKeyRing keyRing = keyrings.next();
@@ -575,30 +598,33 @@ public class BouncyCastleGpgKeyLocator {
// try key id
String fingerprint = Hex.toHexString(key.getFingerprint())
.toLowerCase(Locale.ROOT);
- if (fingerprint.endsWith(keyId)) {
+ if (fingerprint.endsWith(id)) {
return key;
}
// try user id
Iterator<String> userIDs = key.getUserIDs();
while (userIDs.hasNext()) {
String userId = userIDs.next();
- if (containsSigningKey(userId, signingKey)) {
+ if (containsSigningKey(userId, keySpec)) {
return key;
}
}
}
}
+ } catch (FileNotFoundException | NoSuchFileException e) {
+ // Ignore and return null
}
return null;
}
- private PGPPublicKey getPublicKey(KeyBlob blob, byte[] fingerprint)
+ private static PGPPublicKey getPublicKey(KeyBlob blob, byte[] fingerprint)
throws IOException {
return ((PublicKeyRingBlob) blob).getPGPPublicKeyRing()
.getPublicKey(fingerprint);
}
- private PGPPublicKey getSigningPublicKey(KeyBlob blob) throws IOException {
+ private static PGPPublicKey getSigningPublicKey(KeyBlob blob)
+ throws IOException {
PGPPublicKey masterKey = null;
Iterator<PGPPublicKey> keys = ((PublicKeyRingBlob) blob)
.getPGPPublicKeyRing().getPublicKeys();
@@ -618,7 +644,7 @@ public class BouncyCastleGpgKeyLocator {
return masterKey;
}
- private boolean isSigningKey(PGPPublicKey key) {
+ private static boolean isSigningKey(PGPPublicKey key) {
Iterator signatures = key.getSignatures();
while (signatures.hasNext()) {
PGPSignature sig = (PGPSignature) signatures.next();
@@ -630,7 +656,7 @@ public class BouncyCastleGpgKeyLocator {
return false;
}
- private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException,
+ private static KeyBox readKeyBoxFile(Path keyboxFile) throws IOException,
NoSuchAlgorithmException, NoSuchProviderException,
NoOpenPgpKeyException {
if (keyboxFile.toFile().length() == 0) {
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyPassphrasePrompt.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyPassphrasePrompt.java
index e47f64f1a6..6144195983 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyPassphrasePrompt.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyPassphrasePrompt.java
@@ -17,8 +17,8 @@ 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.transport.CredentialItem.CharArrayType;
import org.eclipse.jgit.transport.CredentialItem.InformationalMessage;
+import org.eclipse.jgit.transport.CredentialItem.Password;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.URIish;
@@ -31,7 +31,7 @@ import org.eclipse.jgit.transport.URIish;
*/
class BouncyCastleGpgKeyPassphrasePrompt implements AutoCloseable {
- private CharArrayType passphrase;
+ private Password passphrase;
private CredentialsProvider credentialsProvider;
@@ -78,8 +78,7 @@ class BouncyCastleGpgKeyPassphrasePrompt implements AutoCloseable {
throws PGPException, CanceledException, UnsupportedCredentialItem,
URISyntaxException {
if (passphrase == null) {
- passphrase = new CharArrayType(BCText.get().credentialPassphrase,
- true);
+ passphrase = new Password(BCText.get().credentialPassphrase);
}
if (credentialsProvider == null) {
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java
new file mode 100644
index 0000000000..7161895a6b
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Security;
+import java.text.MessageFormat;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Locale;
+
+import org.bouncycastle.bcpg.sig.IssuerFingerprint;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPCompressedData;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureList;
+import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.bouncycastle.util.encoders.Hex;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.util.LRUMap;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.StringUtils;
+
+/**
+ * A {@link GpgSignatureVerifier} to verify GPG signatures using BouncyCastle.
+ */
+public class BouncyCastleGpgSignatureVerifier implements GpgSignatureVerifier {
+
+ private static void registerBouncyCastleProviderIfNecessary() {
+ if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ }
+
+ /**
+ * Creates a new instance and registers the BouncyCastle security provider
+ * if needed.
+ */
+ public BouncyCastleGpgSignatureVerifier() {
+ registerBouncyCastleProviderIfNecessary();
+ }
+
+ // To support more efficient signature verification of multiple objects we
+ // cache public keys once found in a LRU cache.
+
+ private static final Object NO_KEY = new Object();
+
+ private LRUMap<String, Object> byFingerprint = new LRUMap<>(16, 200);
+
+ private LRUMap<String, Object> bySigner = new LRUMap<>(16, 200);
+
+ @Override
+ public String getName() {
+ return "bc"; //$NON-NLS-1$
+ }
+
+ @Override
+ @Nullable
+ public SignatureVerification verifySignature(@NonNull RevObject object,
+ @NonNull GpgConfig config) throws IOException {
+ if (object instanceof RevCommit) {
+ RevCommit commit = (RevCommit) object;
+ byte[] signatureData = commit.getRawGpgSignature();
+ if (signatureData == null) {
+ return null;
+ }
+ byte[] raw = commit.getRawBuffer();
+ // Now remove the GPG signature
+ byte[] header = { 'g', 'p', 'g', 's', 'i', 'g' };
+ int start = RawParseUtils.headerStart(header, raw, 0);
+ if (start < 0) {
+ return null;
+ }
+ int end = RawParseUtils.headerEnd(raw, start);
+ // start is at the beginning of the header's content
+ start -= header.length + 1;
+ // end is on the terminating LF; we need to skip that, too
+ if (end < raw.length) {
+ end++;
+ }
+ byte[] data = new byte[raw.length - (end - start)];
+ System.arraycopy(raw, 0, data, 0, start);
+ System.arraycopy(raw, end, data, start, raw.length - end);
+ return verify(data, signatureData);
+ } else if (object instanceof RevTag) {
+ RevTag tag = (RevTag) object;
+ byte[] signatureData = tag.getRawGpgSignature();
+ if (signatureData == null) {
+ return null;
+ }
+ byte[] raw = tag.getRawBuffer();
+ // The signature is just tacked onto the end of the message, which
+ // is last in the buffer.
+ byte[] data = Arrays.copyOfRange(raw, 0,
+ raw.length - signatureData.length);
+ return verify(data, signatureData);
+ }
+ return null;
+ }
+
+ static PGPSignature parseSignature(InputStream in)
+ throws IOException, PGPException {
+ try (InputStream sigIn = PGPUtil.getDecoderStream(in)) {
+ JcaPGPObjectFactory pgpFactory = new JcaPGPObjectFactory(sigIn);
+ Object obj = pgpFactory.nextObject();
+ if (obj instanceof PGPCompressedData) {
+ obj = new JcaPGPObjectFactory(
+ ((PGPCompressedData) obj).getDataStream()).nextObject();
+ }
+ if (obj instanceof PGPSignatureList) {
+ return ((PGPSignatureList) obj).get(0);
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public SignatureVerification verify(byte[] data, byte[] signatureData)
+ throws IOException {
+ PGPSignature signature = null;
+ String fingerprint = null;
+ String signer = null;
+ String keyId = null;
+ try (InputStream sigIn = new ByteArrayInputStream(signatureData)) {
+ signature = parseSignature(sigIn);
+ if (signature != null) {
+ // Try to figure out something to find the public key with.
+ if (signature.hasSubpackets()) {
+ PGPSignatureSubpacketVector packets = signature
+ .getHashedSubPackets();
+ IssuerFingerprint fingerprintPacket = packets
+ .getIssuerFingerprint();
+ if (fingerprintPacket != null) {
+ fingerprint = Hex
+ .toHexString(fingerprintPacket.getFingerprint())
+ .toLowerCase(Locale.ROOT);
+ }
+ signer = packets.getSignerUserID();
+ if (signer != null) {
+ signer = BouncyCastleGpgSigner.extractSignerId(signer);
+ }
+ }
+ keyId = Long.toUnsignedString(signature.getKeyID(), 16)
+ .toLowerCase(Locale.ROOT);
+ } else {
+ throw new JGitInternalException(BCText.get().nonSignatureError);
+ }
+ } catch (PGPException e) {
+ throw new JGitInternalException(BCText.get().signatureParseError,
+ e);
+ }
+ Date signatureCreatedAt = signature.getCreationTime();
+ if (fingerprint == null && signer == null && keyId == null) {
+ return new VerificationResult(signatureCreatedAt, null, null, null,
+ false, false, TrustLevel.UNKNOWN,
+ BCText.get().signatureNoKeyInfo);
+ }
+ if (fingerprint != null && keyId != null
+ && !fingerprint.endsWith(keyId)) {
+ return new VerificationResult(signatureCreatedAt, signer, fingerprint,
+ null, false, false, TrustLevel.UNKNOWN,
+ MessageFormat.format(BCText.get().signatureInconsistent,
+ keyId, fingerprint));
+ }
+ if (fingerprint == null && keyId != null) {
+ fingerprint = keyId;
+ }
+ // Try to find the public key
+ String keySpec = '<' + signer + '>';
+ Object cached = null;
+ PGPPublicKey publicKey = null;
+ try {
+ cached = byFingerprint.get(fingerprint);
+ if (cached != null) {
+ if (cached instanceof PGPPublicKey) {
+ publicKey = (PGPPublicKey) cached;
+ }
+ } else if (!StringUtils.isEmptyOrNull(signer)) {
+ cached = bySigner.get(signer);
+ if (cached != null) {
+ if (cached instanceof PGPPublicKey) {
+ publicKey = (PGPPublicKey) cached;
+ }
+ }
+ }
+ if (cached == null) {
+ publicKey = BouncyCastleGpgKeyLocator.findPublicKey(fingerprint,
+ keySpec);
+ }
+ } catch (IOException | PGPException e) {
+ throw new JGitInternalException(
+ BCText.get().signatureKeyLookupError, e);
+ }
+ if (publicKey == null) {
+ if (cached == null) {
+ byFingerprint.put(fingerprint, NO_KEY);
+ byFingerprint.put(keyId, NO_KEY);
+ if (signer != null) {
+ bySigner.put(signer, NO_KEY);
+ }
+ }
+ return new VerificationResult(signatureCreatedAt, signer,
+ fingerprint, null, false, false, TrustLevel.UNKNOWN,
+ BCText.get().signatureNoPublicKey);
+ }
+ if (cached == null) {
+ byFingerprint.put(fingerprint, publicKey);
+ byFingerprint.put(keyId, publicKey);
+ if (signer != null) {
+ bySigner.put(signer, publicKey);
+ }
+ }
+ String user = null;
+ Iterator<String> userIds = publicKey.getUserIDs();
+ if (!StringUtils.isEmptyOrNull(signer)) {
+ while (userIds.hasNext()) {
+ String userId = userIds.next();
+ if (BouncyCastleGpgKeyLocator.containsSigningKey(userId,
+ keySpec)) {
+ user = userId;
+ break;
+ }
+ }
+ }
+ if (user == null) {
+ userIds = publicKey.getUserIDs();
+ if (userIds.hasNext()) {
+ user = userIds.next();
+ }
+ }
+ boolean expired = false;
+ long validFor = publicKey.getValidSeconds();
+ if (validFor > 0 && signatureCreatedAt != null) {
+ Instant expiredAt = publicKey.getCreationTime().toInstant()
+ .plusSeconds(validFor);
+ expired = expiredAt.isBefore(signatureCreatedAt.toInstant());
+ }
+ // Trust data is not defined in OpenPGP; the format is implementation
+ // specific. We don't use the GPG trustdb but simply the trust packet
+ // on the public key, if present. Even if present, it may or may not
+ // be set.
+ byte[] trustData = publicKey.getTrustData();
+ TrustLevel trust = parseGpgTrustPacket(trustData);
+ boolean verified = false;
+ try {
+ signature.init(
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(BouncyCastleProvider.PROVIDER_NAME),
+ publicKey);
+ signature.update(data);
+ verified = signature.verify();
+ } catch (PGPException e) {
+ throw new JGitInternalException(
+ BCText.get().signatureVerificationError, e);
+ }
+ return new VerificationResult(signatureCreatedAt, signer, fingerprint, user,
+ verified, expired, trust, null);
+ }
+
+ private TrustLevel parseGpgTrustPacket(byte[] packet) {
+ if (packet == null || packet.length < 6) {
+ // A GPG trust packet has at least 6 bytes.
+ return TrustLevel.UNKNOWN;
+ }
+ if (packet[2] != 'g' || packet[3] != 'p' || packet[4] != 'g') {
+ // Not a GPG trust packet
+ return TrustLevel.UNKNOWN;
+ }
+ int trust = packet[0] & 0x0F;
+ switch (trust) {
+ case 0: // No determined/set
+ case 1: // Trust expired; i.e., calculation outdated or key expired
+ case 2: // Undefined: not enough information to set
+ return TrustLevel.UNKNOWN;
+ case 3:
+ return TrustLevel.NEVER;
+ case 4:
+ return TrustLevel.MARGINAL;
+ case 5:
+ return TrustLevel.FULL;
+ case 6:
+ return TrustLevel.ULTIMATE;
+ default:
+ return TrustLevel.UNKNOWN;
+ }
+ }
+
+ @Override
+ public void clear() {
+ byFingerprint.clear();
+ bySigner.clear();
+ }
+
+ private static class VerificationResult implements SignatureVerification {
+
+ private final Date creationDate;
+
+ private final String signer;
+
+ private final String keyUser;
+
+ private final String fingerprint;
+
+ private final boolean verified;
+
+ private final boolean expired;
+
+ private final @NonNull TrustLevel trustLevel;
+
+ private final String message;
+
+ public VerificationResult(Date creationDate, String signer,
+ String fingerprint, String user, boolean verified,
+ boolean expired, @NonNull TrustLevel trust, String message) {
+ this.creationDate = creationDate;
+ this.signer = signer;
+ this.fingerprint = fingerprint;
+ this.keyUser = user;
+ this.verified = verified;
+ this.expired = expired;
+ this.trustLevel = trust;
+ this.message = message;
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return creationDate;
+ }
+
+ @Override
+ public String getSigner() {
+ return signer;
+ }
+
+ @Override
+ public String getKeyUser() {
+ return keyUser;
+ }
+
+ @Override
+ public String getKeyFingerprint() {
+ return fingerprint;
+ }
+
+ @Override
+ public boolean isExpired() {
+ return expired;
+ }
+
+ @Override
+ public TrustLevel getTrustLevel() {
+ return trustLevel;
+ }
+
+ @Override
+ public String getMessage() {
+ return message;
+ }
+
+ @Override
+ public boolean getVerified() {
+ return verified;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifierFactory.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifierFactory.java
new file mode 100644
index 0000000000..ae82b758a6
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifierFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal;
+
+import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
+
+/**
+ * A {@link GpgSignatureVerifierFactory} that creates
+ * {@link GpgSignatureVerifier} instances that verify GPG signatures using
+ * BouncyCastle and that do cache public keys.
+ */
+public final class BouncyCastleGpgSignatureVerifierFactory
+ extends GpgSignatureVerifierFactory {
+
+ @Override
+ public GpgSignatureVerifier getVerifier() {
+ return new BouncyCastleGpgSignatureVerifier();
+ }
+
+}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
index ea159c547d..211bd7bd20 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2020, Salesforce and others
+ * Copyright (C) 2018, 2021, Salesforce and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -34,18 +34,25 @@ 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.api.errors.UnsupportedSigningFormatException;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgObjectSigner;
import org.eclipse.jgit.lib.GpgSignature;
import org.eclipse.jgit.lib.GpgSigner;
+import org.eclipse.jgit.lib.ObjectBuilder;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.util.StringUtils;
/**
- * GPG Signer using BouncyCastle library
+ * GPG Signer using the BouncyCastle library.
*/
-public class BouncyCastleGpgSigner extends GpgSigner {
+public class BouncyCastleGpgSigner extends GpgSigner
+ implements GpgObjectSigner {
private static void registerBouncyCastleProviderIfNecessary() {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
@@ -67,13 +74,32 @@ public class BouncyCastleGpgSigner extends GpgSigner {
public boolean canLocateSigningKey(@Nullable String gpgSigningKey,
PersonIdent committer, CredentialsProvider credentialsProvider)
throws CanceledException {
+ try {
+ return canLocateSigningKey(gpgSigningKey, committer,
+ credentialsProvider, null);
+ } catch (UnsupportedSigningFormatException e) {
+ // Cannot occur with a null config
+ return false;
+ }
+ }
+
+ @Override
+ public boolean canLocateSigningKey(@Nullable String gpgSigningKey,
+ PersonIdent committer, CredentialsProvider credentialsProvider,
+ GpgConfig config)
+ throws CanceledException, UnsupportedSigningFormatException {
+ if (config != null && config.getKeyFormat() != GpgFormat.OPENPGP) {
+ throw new UnsupportedSigningFormatException(
+ JGitText.get().onlyOpenPgpSupportedForSigning);
+ }
try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
credentialsProvider)) {
BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
committer, passphrasePrompt);
return gpgKey != null;
- } catch (PGPException | IOException | NoSuchAlgorithmException
- | NoSuchProviderException | URISyntaxException e) {
+ } catch (CanceledException e) {
+ throw e;
+ } catch (Exception e) {
return false;
}
}
@@ -98,10 +124,28 @@ public class BouncyCastleGpgSigner extends GpgSigner {
public void sign(@NonNull CommitBuilder commit,
@Nullable String gpgSigningKey, @NonNull PersonIdent committer,
CredentialsProvider credentialsProvider) throws CanceledException {
+ try {
+ signObject(commit, gpgSigningKey, committer, credentialsProvider,
+ null);
+ } catch (UnsupportedSigningFormatException e) {
+ // Cannot occur with a null config
+ }
+ }
+
+ @Override
+ public void signObject(@NonNull ObjectBuilder object,
+ @Nullable String gpgSigningKey, @NonNull PersonIdent committer,
+ CredentialsProvider credentialsProvider, GpgConfig config)
+ throws CanceledException, UnsupportedSigningFormatException {
+ if (config != null && config.getKeyFormat() != GpgFormat.OPENPGP) {
+ throw new UnsupportedSigningFormatException(
+ JGitText.get().onlyOpenPgpSupportedForSigning);
+ }
try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
credentialsProvider)) {
BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
- committer, passphrasePrompt);
+ committer,
+ passphrasePrompt);
PGPSecretKey secretKey = gpgKey.getSecretKey();
if (secretKey == null) {
throw new JGitInternalException(
@@ -158,17 +202,17 @@ public class BouncyCastleGpgSigner extends GpgSigner {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (BCPGOutputStream out = new BCPGOutputStream(
new ArmoredOutputStream(buffer))) {
- signatureGenerator.update(commit.build());
+ signatureGenerator.update(object.build());
signatureGenerator.generate().encode(out);
}
- commit.setGpgSignature(new GpgSignature(buffer.toByteArray()));
+ object.setGpgSignature(new GpgSignature(buffer.toByteArray()));
} catch (PGPException | IOException | NoSuchAlgorithmException
| NoSuchProviderException | URISyntaxException e) {
throw new JGitInternalException(e.getMessage(), e);
}
}
- private String extractSignerId(String pgpUserId) {
+ static String extractSignerId(String pgpUserId) {
int from = pgpUserId.indexOf('<');
if (from >= 0) {
int to = pgpUserId.indexOf('>', from + 1);
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip.java
new file mode 100644
index 0000000000..b1d4446010
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.Arrays;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.bcpg.DSAPublicBCPGKey;
+import org.bouncycastle.bcpg.ECPublicBCPGKey;
+import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.bcpg.RSAPublicBCPGKey;
+import org.bouncycastle.crypto.ec.CustomNamedCurves;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.field.FiniteField;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.util.encoders.Hex;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.gpg.bc.internal.BCText;
+import org.eclipse.jgit.util.sha1.SHA1;
+
+/**
+ * Utilities to compute the <em>keygrip</em> of a key. A keygrip is a SHA1 hash
+ * over the public key parameters and is used internally by the gpg-agent to
+ * find the secret key belonging to a public key: the secret key is stored in a
+ * file under ~/.gnupg/private-keys-v1.d/ with a name "&lt;keygrip>.key". While
+ * this storage organization is an implementation detail of GPG, the way
+ * keygrips are computed is not; they are computed by libgcrypt and their
+ * definition is stable.
+ */
+public final class KeyGrip {
+
+ // Some OIDs apparently unknown to BouncyCastle.
+
+ private static String OID_OPENPGP_ED25519 = "1.3.6.1.4.1.11591.15.1"; //$NON-NLS-1$
+
+ private static String OID_RFC8410_CURVE25519 = "1.3.101.110"; //$NON-NLS-1$
+
+ private static String OID_RFC8410_ED25519 = "1.3.101.112"; //$NON-NLS-1$
+
+ private KeyGrip() {
+ // No instantiation
+ }
+
+ /**
+ * Computes the keygrip for a {@link PGPPublicKey}.
+ *
+ * @param publicKey
+ * to get the keygrip of
+ * @return the keygrip
+ * @throws PGPException
+ * if an unknown key type is encountered.
+ */
+ @NonNull
+ public static byte[] getKeyGrip(PGPPublicKey publicKey)
+ throws PGPException {
+ SHA1 grip = SHA1.newInstance();
+ grip.setDetectCollision(false);
+
+ switch (publicKey.getAlgorithm()) {
+ case PublicKeyAlgorithmTags.RSA_GENERAL:
+ case PublicKeyAlgorithmTags.RSA_ENCRYPT:
+ case PublicKeyAlgorithmTags.RSA_SIGN:
+ BigInteger modulus = ((RSAPublicBCPGKey) publicKey
+ .getPublicKeyPacket().getKey()).getModulus();
+ hash(grip, modulus.toByteArray());
+ break;
+ case PublicKeyAlgorithmTags.DSA:
+ DSAPublicBCPGKey dsa = (DSAPublicBCPGKey) publicKey
+ .getPublicKeyPacket().getKey();
+ hash(grip, dsa.getP().toByteArray(), 'p', true);
+ hash(grip, dsa.getQ().toByteArray(), 'q', true);
+ hash(grip, dsa.getG().toByteArray(), 'g', true);
+ hash(grip, dsa.getY().toByteArray(), 'y', true);
+ break;
+ case PublicKeyAlgorithmTags.ELGAMAL_GENERAL:
+ case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
+ ElGamalPublicBCPGKey eg = (ElGamalPublicBCPGKey) publicKey
+ .getPublicKeyPacket().getKey();
+ hash(grip, eg.getP().toByteArray(), 'p', true);
+ hash(grip, eg.getG().toByteArray(), 'g', true);
+ hash(grip, eg.getY().toByteArray(), 'y', true);
+ break;
+ case PublicKeyAlgorithmTags.ECDH:
+ case PublicKeyAlgorithmTags.ECDSA:
+ case PublicKeyAlgorithmTags.EDDSA:
+ ECPublicBCPGKey ec = (ECPublicBCPGKey) publicKey
+ .getPublicKeyPacket().getKey();
+ ASN1ObjectIdentifier curveOID = ec.getCurveOID();
+ // BC doesn't know these OIDs.
+ if (OID_OPENPGP_ED25519.equals(curveOID.getId())
+ || OID_RFC8410_ED25519.equals(curveOID.getId())) {
+ return hashEd25519(grip, ec.getEncodedPoint());
+ } else if (CryptlibObjectIdentifiers.curvey25519.equals(curveOID)
+ || OID_RFC8410_CURVE25519.equals(curveOID.getId())) {
+ // curvey25519 actually is the OpenPGP OID for Curve25519 and is
+ // known to BC, but the parameters are for the short Weierstrass
+ // form. See https://github.com/bcgit/bc-java/issues/399 .
+ // libgcrypt uses Montgomery form.
+ return hashCurve25519(grip, ec.getEncodedPoint());
+ }
+ X9ECParameters params = getX9Parameters(curveOID);
+ if (params == null) {
+ throw new PGPException(MessageFormat
+ .format(BCText.get().unknownCurve, curveOID.getId()));
+ }
+ // Need to write p, a, b, g, n, q
+ BigInteger q = ec.getEncodedPoint();
+ byte[] g = params.getG().getEncoded(false);
+ BigInteger a = params.getCurve().getA().toBigInteger();
+ BigInteger b = params.getCurve().getB().toBigInteger();
+ BigInteger n = params.getN();
+ BigInteger p = null;
+ FiniteField field = params.getCurve().getField();
+ if (ECAlgorithms.isFpField(field)) {
+ p = field.getCharacteristic();
+ }
+ if (p == null) {
+ // Don't know...
+ throw new PGPException(MessageFormat.format(
+ BCText.get().unknownCurveParameters, curveOID.getId()));
+ }
+ hash(grip, p.toByteArray(), 'p', false);
+ hash(grip, a.toByteArray(), 'a', false);
+ hash(grip, b.toByteArray(), 'b', false);
+ hash(grip, g, 'g', false);
+ hash(grip, n.toByteArray(), 'n', false);
+ if (publicKey.getAlgorithm() == PublicKeyAlgorithmTags.EDDSA) {
+ hashQ25519(grip, q);
+ } else {
+ hash(grip, q.toByteArray(), 'q', false);
+ }
+ break;
+ default:
+ throw new PGPException(
+ MessageFormat.format(BCText.get().unknownKeyType,
+ Integer.toString(publicKey.getAlgorithm())));
+ }
+ return grip.digest();
+ }
+
+ private static void hash(SHA1 grip, byte[] data) {
+ // Need to skip leading zero bytes
+ int i = 0;
+ while (i < data.length && data[i] == 0) {
+ i++;
+ }
+ int length = data.length - i;
+ if (i < data.length) {
+ if ((data[i] & 0x80) != 0) {
+ grip.update((byte) 0);
+ }
+ grip.update(data, i, length);
+ }
+ }
+
+ private static void hash(SHA1 grip, byte[] data, char id, boolean zeroPad) {
+ // Need to skip leading zero bytes
+ int i = 0;
+ while (i < data.length && data[i] == 0) {
+ i++;
+ }
+ int length = data.length - i;
+ boolean addZero = false;
+ if (i < data.length && zeroPad && (data[i] & 0x80) != 0) {
+ addZero = true;
+ }
+ // libgcrypt includes an SExp in the hash
+ String prefix = "(1:" + id + (addZero ? length + 1 : length) + ':'; //$NON-NLS-1$
+ grip.update(prefix.getBytes(StandardCharsets.US_ASCII));
+ // For some items, gcrypt prepends a zero byte if the high bit is set
+ if (addZero) {
+ grip.update((byte) 0);
+ }
+ if (i < data.length) {
+ grip.update(data, i, length);
+ }
+ grip.update((byte) ')');
+ }
+
+ private static void hashQ25519(SHA1 grip, BigInteger q)
+ throws PGPException {
+ byte[] data = q.toByteArray();
+ switch (data[0]) {
+ case 0x04:
+ if (data.length != 65) {
+ throw new PGPException(MessageFormat.format(
+ BCText.get().corrupt25519Key, Hex.toHexString(data)));
+ }
+ // Uncompressed: should not occur with ed25519 or curve25519
+ throw new PGPException(MessageFormat.format(
+ BCText.get().uncompressed25519Key, Hex.toHexString(data)));
+ case 0x40:
+ if (data.length != 33) {
+ throw new PGPException(MessageFormat.format(
+ BCText.get().corrupt25519Key, Hex.toHexString(data)));
+ }
+ // Compressed; normal case. Skip prefix.
+ hash(grip, Arrays.copyOfRange(data, 1, data.length), 'q', false);
+ break;
+ default:
+ if (data.length != 32) {
+ throw new PGPException(MessageFormat.format(
+ BCText.get().corrupt25519Key, Hex.toHexString(data)));
+ }
+ // Compressed format without prefix. Should not occur?
+ hash(grip, data, 'q', false);
+ break;
+ }
+ }
+
+ /**
+ * Computes the keygrip for an ed25519 public key.
+ * <p>
+ * Package-visible for tests only.
+ * </p>
+ *
+ * @param grip
+ * initialized {@link SHA1}
+ * @param q
+ * the public key's EC point
+ * @return the keygrip
+ * @throws PGPException
+ * if q indicates uncompressed format
+ */
+ @SuppressWarnings("nls")
+ static byte[] hashEd25519(SHA1 grip, BigInteger q) throws PGPException {
+ // For the values, see RFC 7748: https://tools.ietf.org/html/rfc7748
+ // p = 2^255 - 19
+ hash(grip, Hex.decodeStrict(
+ "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED"),
+ 'p', false);
+ // Field: a = 1
+ hash(grip, new byte[] { 0x01 }, 'a', false);
+ // Field: b = 121665/121666 (mod p)
+ // See Berstein et.al., "Twisted Edwards Curves",
+ // https://doi.org/10.1007/978-3-540-68164-9_26
+ hash(grip, Hex.decodeStrict(
+ "2DFC9311D490018C7338BF8688861767FF8FF5B2BEBE27548A14B235ECA6874A"),
+ 'b', false);
+ // Generator point with affine X,Y
+ // @formatter:off
+ // X(P) = 15112221349535400772501151409588531511454012693041857206046113283949847762202
+ // Y(P) = 46316835694926478169428394003475163141307993866256225615783033603165251855960
+ // the "04" signifies uncompressed format.
+ // @formatter:on
+ hash(grip, Hex.decodeStrict("04"
+ + "216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A"
+ + "6666666666666666666666666666666666666666666666666666666666666658"),
+ 'g', false);
+ // order = 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed
+ hash(grip, Hex.decodeStrict(
+ "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"),
+ 'n', false);
+ hashQ25519(grip, q);
+ return grip.digest();
+ }
+
+ /**
+ * Computes the keygrip for a curve25519 public key.
+ * <p>
+ * Package-visible for tests only.
+ * </p>
+ *
+ * @param grip
+ * initialized {@link SHA1}
+ * @param q
+ * the public key's EC point
+ * @return the keygrip
+ * @throws PGPException
+ * if q indicates uncompressed format
+ */
+ @SuppressWarnings("nls")
+ static byte[] hashCurve25519(SHA1 grip, BigInteger q) throws PGPException {
+ hash(grip, Hex.decodeStrict(
+ "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED"),
+ 'p', false);
+ // Unclear: RFC 7748 says A = 486662. This value here is (A-2)/4 =
+ // 121665. Compare ecc-curves.c in libgcrypt:
+ // https://github.com/gpg/libgcrypt/blob/361a058/cipher/ecc-curves.c#L146
+ hash(grip, new byte[] { 0x01, (byte) 0xDB, 0x41 }, 'a', false);
+ hash(grip, new byte[] { 0x01 }, 'b', false);
+ // libgcrypt uses the old g.y value before the erratum to RFC 7748 for
+ // the keygrip. The new value would be
+ // 5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14. See
+ // https://www.rfc-editor.org/errata/eid4730 and
+ // https://github.com/gpg/libgcrypt/commit/f67b6492e0b0
+ hash(grip, Hex.decodeStrict("04"
+ + "0000000000000000000000000000000000000000000000000000000000000009"
+ + "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9"),
+ 'g', false);
+ hash(grip, Hex.decodeStrict(
+ "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"),
+ 'n', false);
+ hashQ25519(grip, q);
+ return grip.digest();
+ }
+
+ private static X9ECParameters getX9Parameters(
+ ASN1ObjectIdentifier curveOID) {
+ X9ECParameters params = CustomNamedCurves.getByOID(curveOID);
+ if (params == null) {
+ params = ECNamedCurveTable.getByOID(curveOID);
+ }
+ return params;
+ }
+
+}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/OCBPBEProtectionRemoverFactory.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/OCBPBEProtectionRemoverFactory.java
new file mode 100644
index 0000000000..68f8a45555
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/OCBPBEProtectionRemoverFactory.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import java.security.NoSuchAlgorithmException;
+import java.text.MessageFormat;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
+import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
+import org.bouncycastle.util.Arrays;
+import org.eclipse.jgit.gpg.bc.internal.BCText;
+
+/**
+ * A {@link PBEProtectionRemoverFactory} using AES/OCB/NoPadding for decryption.
+ * It accepts an AAD in the factory's constructor, so the factory can be used to
+ * create a {@link PBESecretKeyDecryptor} only for a particular input.
+ * <p>
+ * For JGit's needs, this is sufficient, but for a general upstream
+ * implementation that limitation might not be acceptable.
+ * </p>
+ */
+class OCBPBEProtectionRemoverFactory
+ implements PBEProtectionRemoverFactory {
+
+ private final PGPDigestCalculatorProvider calculatorProvider;
+
+ private final char[] passphrase;
+
+ private final byte[] aad;
+
+ /**
+ * Creates a new factory instance with the given parameters.
+ * <p>
+ * Because the AAD is given at factory level, the {@link PBESecretKeyDecryptor}s
+ * created by the factory can be used to decrypt only a particular input
+ * matching this AAD.
+ * </p>
+ *
+ * @param passphrase to use for secret key derivation
+ * @param calculatorProvider for computing digests
+ * @param aad for the OCB decryption
+ */
+ OCBPBEProtectionRemoverFactory(char[] passphrase,
+ PGPDigestCalculatorProvider calculatorProvider, byte[] aad) {
+ this.calculatorProvider = calculatorProvider;
+ this.passphrase = passphrase;
+ this.aad = aad;
+ }
+
+ @Override
+ public PBESecretKeyDecryptor createDecryptor(String protection)
+ throws PGPException {
+ return new PBESecretKeyDecryptor(passphrase, calculatorProvider) {
+
+ @Override
+ public byte[] recoverKeyData(int encAlgorithm, byte[] key,
+ byte[] iv, byte[] encrypted, int encryptedOffset,
+ int encryptedLength) throws PGPException {
+ String algorithmName = PGPUtil
+ .getSymmetricCipherName(encAlgorithm);
+ byte[] decrypted = null;
+ try {
+ Cipher c = Cipher
+ .getInstance(algorithmName + "/OCB/NoPadding"); //$NON-NLS-1$
+ SecretKey secretKey = new SecretKeySpec(key, algorithmName);
+ c.init(Cipher.DECRYPT_MODE, secretKey,
+ new IvParameterSpec(iv));
+ c.updateAAD(aad);
+ decrypted = new byte[c.getOutputSize(encryptedLength)];
+ int decryptedLength = c.update(encrypted, encryptedOffset,
+ encryptedLength, decrypted);
+ // doFinal() for OCB will check the MAC and throw an
+ // exception if it doesn't match
+ decryptedLength += c.doFinal(decrypted, decryptedLength);
+ if (decryptedLength != decrypted.length) {
+ throw new PGPException(MessageFormat.format(
+ BCText.get().cryptWrongDecryptedLength,
+ Integer.valueOf(decryptedLength),
+ Integer.valueOf(decrypted.length)));
+ }
+ byte[] result = decrypted;
+ decrypted = null; // Don't clear in finally
+ return result;
+ } catch (NoClassDefFoundError e) {
+ String msg = MessageFormat.format(
+ BCText.get().gpgNoSuchAlgorithm,
+ algorithmName + "/OCB"); //$NON-NLS-1$
+ throw new PGPException(msg,
+ new NoSuchAlgorithmException(msg, e));
+ } catch (PGPException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new PGPException(
+ MessageFormat.format(BCText.get().cryptCipherError,
+ e.getLocalizedMessage()),
+ e);
+ } finally {
+ if (decrypted != null) {
+ // Prevent halfway decrypted data leaking.
+ Arrays.fill(decrypted, (byte) 0);
+ }
+ }
+ }
+ };
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SExprParser.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SExprParser.java
new file mode 100644
index 0000000000..a9bb22c780
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SExprParser.java
@@ -0,0 +1,826 @@
+/*
+ * Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org)
+ * <p>
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ *including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * </p>
+ * <p>
+ * The above copyright notice and this permission notice shall be included in all copies or substantial
+ * portions of the Software.
+ * </p>
+ * <p>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * </p>
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.Date;
+
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.bcpg.DSAPublicBCPGKey;
+import org.bouncycastle.bcpg.DSASecretBCPGKey;
+import org.bouncycastle.bcpg.ECDSAPublicBCPGKey;
+import org.bouncycastle.bcpg.ECPublicBCPGKey;
+import org.bouncycastle.bcpg.ECSecretBCPGKey;
+import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
+import org.bouncycastle.bcpg.ElGamalSecretBCPGKey;
+import org.bouncycastle.bcpg.HashAlgorithmTags;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.bcpg.PublicKeyPacket;
+import org.bouncycastle.bcpg.RSAPublicBCPGKey;
+import org.bouncycastle.bcpg.RSASecretBCPGKey;
+import org.bouncycastle.bcpg.S2K;
+import org.bouncycastle.bcpg.SecretKeyPacket;
+import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPSecretKey;
+import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
+import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
+import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
+import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * A parser for secret keys stored in s-expressions. Original BouncyCastle code
+ * modified by the JGit team to:
+ * <ul>
+ * <li>handle unencrypted DSA, EC, and ElGamal keys (upstream only handles
+ * unencrypted RSA), and</li>
+ * <li>handle secret keys using AES/OCB as encryption (those don't have a
+ * hash).</li>
+ * </ul>
+ */
+@SuppressWarnings("nls")
+public class SExprParser {
+ private final PGPDigestCalculatorProvider digestProvider;
+
+ /**
+ * Base constructor.
+ *
+ * @param digestProvider
+ * a provider for digest calculations. Used to confirm key
+ * protection hashes.
+ */
+ public SExprParser(PGPDigestCalculatorProvider digestProvider) {
+ this.digestProvider = digestProvider;
+ }
+
+ /**
+ * Parse a secret key from one of the GPG S expression keys associating it
+ * with the passed in public key.
+ *
+ * @param inputStream
+ * to read from
+ * @param keyProtectionRemoverFactory
+ * for decrypting encrypted keys
+ * @param pubKey
+ * the private key should belong to
+ *
+ * @return a secret key object.
+ * @throws IOException
+ * @throws PGPException
+ */
+ public PGPSecretKey parseSecretKey(InputStream inputStream,
+ PBEProtectionRemoverFactory keyProtectionRemoverFactory,
+ PGPPublicKey pubKey) throws IOException, PGPException {
+ SXprUtils.skipOpenParenthesis(inputStream);
+
+ String type;
+
+ type = SXprUtils.readString(inputStream, inputStream.read());
+ if (type.equals("protected-private-key")
+ || type.equals("private-key")) {
+ SXprUtils.skipOpenParenthesis(inputStream);
+
+ String keyType = SXprUtils.readString(inputStream,
+ inputStream.read());
+ if (keyType.equals("ecc")) {
+ SXprUtils.skipOpenParenthesis(inputStream);
+
+ String curveID = SXprUtils.readString(inputStream,
+ inputStream.read());
+ String curveName = SXprUtils.readString(inputStream,
+ inputStream.read());
+
+ SXprUtils.skipCloseParenthesis(inputStream);
+
+ byte[] qVal;
+
+ SXprUtils.skipOpenParenthesis(inputStream);
+
+ type = SXprUtils.readString(inputStream, inputStream.read());
+ if (type.equals("q")) {
+ qVal = SXprUtils.readBytes(inputStream, inputStream.read());
+ } else {
+ throw new PGPException("no q value found");
+ }
+
+ SXprUtils.skipCloseParenthesis(inputStream);
+
+ BigInteger d = processECSecretKey(inputStream, curveID,
+ curveName, qVal, keyProtectionRemoverFactory);
+
+ if (curveName.startsWith("NIST ")) {
+ curveName = curveName.substring("NIST ".length());
+ }
+
+ ECPublicBCPGKey basePubKey = new ECDSAPublicBCPGKey(
+ ECNamedCurveTable.getOID(curveName),
+ new BigInteger(1, qVal));
+ ECPublicBCPGKey assocPubKey = (ECPublicBCPGKey) pubKey
+ .getPublicKeyPacket().getKey();
+ if (!basePubKey.getCurveOID().equals(assocPubKey.getCurveOID())
+ || !basePubKey.getEncodedPoint()
+ .equals(assocPubKey.getEncodedPoint())) {
+ throw new PGPException(
+ "passed in public key does not match secret key");
+ }
+
+ return new PGPSecretKey(
+ new SecretKeyPacket(pubKey.getPublicKeyPacket(),
+ SymmetricKeyAlgorithmTags.NULL, null, null,
+ new ECSecretBCPGKey(d).getEncoded()),
+ pubKey);
+ } else if (keyType.equals("dsa")) {
+ BigInteger p = readBigInteger("p", inputStream);
+ BigInteger q = readBigInteger("q", inputStream);
+ BigInteger g = readBigInteger("g", inputStream);
+
+ BigInteger y = readBigInteger("y", inputStream);
+
+ BigInteger x = processDSASecretKey(inputStream, p, q, g, y,
+ keyProtectionRemoverFactory);
+
+ DSAPublicBCPGKey basePubKey = new DSAPublicBCPGKey(p, q, g, y);
+ DSAPublicBCPGKey assocPubKey = (DSAPublicBCPGKey) pubKey
+ .getPublicKeyPacket().getKey();
+ if (!basePubKey.getP().equals(assocPubKey.getP())
+ || !basePubKey.getQ().equals(assocPubKey.getQ())
+ || !basePubKey.getG().equals(assocPubKey.getG())
+ || !basePubKey.getY().equals(assocPubKey.getY())) {
+ throw new PGPException(
+ "passed in public key does not match secret key");
+ }
+ return new PGPSecretKey(
+ new SecretKeyPacket(pubKey.getPublicKeyPacket(),
+ SymmetricKeyAlgorithmTags.NULL, null, null,
+ new DSASecretBCPGKey(x).getEncoded()),
+ pubKey);
+ } else if (keyType.equals("elg")) {
+ BigInteger p = readBigInteger("p", inputStream);
+ BigInteger g = readBigInteger("g", inputStream);
+
+ BigInteger y = readBigInteger("y", inputStream);
+
+ BigInteger x = processElGamalSecretKey(inputStream, p, g, y,
+ keyProtectionRemoverFactory);
+
+ ElGamalPublicBCPGKey basePubKey = new ElGamalPublicBCPGKey(p, g,
+ y);
+ ElGamalPublicBCPGKey assocPubKey = (ElGamalPublicBCPGKey) pubKey
+ .getPublicKeyPacket().getKey();
+ if (!basePubKey.getP().equals(assocPubKey.getP())
+ || !basePubKey.getG().equals(assocPubKey.getG())
+ || !basePubKey.getY().equals(assocPubKey.getY())) {
+ throw new PGPException(
+ "passed in public key does not match secret key");
+ }
+
+ return new PGPSecretKey(
+ new SecretKeyPacket(pubKey.getPublicKeyPacket(),
+ SymmetricKeyAlgorithmTags.NULL, null, null,
+ new ElGamalSecretBCPGKey(x).getEncoded()),
+ pubKey);
+ } else if (keyType.equals("rsa")) {
+ BigInteger n = readBigInteger("n", inputStream);
+ BigInteger e = readBigInteger("e", inputStream);
+
+ BigInteger[] values = processRSASecretKey(inputStream, n, e,
+ keyProtectionRemoverFactory);
+
+ // TODO: type of RSA key?
+ RSAPublicBCPGKey basePubKey = new RSAPublicBCPGKey(n, e);
+ RSAPublicBCPGKey assocPubKey = (RSAPublicBCPGKey) pubKey
+ .getPublicKeyPacket().getKey();
+ if (!basePubKey.getModulus().equals(assocPubKey.getModulus())
+ || !basePubKey.getPublicExponent()
+ .equals(assocPubKey.getPublicExponent())) {
+ throw new PGPException(
+ "passed in public key does not match secret key");
+ }
+
+ return new PGPSecretKey(new SecretKeyPacket(
+ pubKey.getPublicKeyPacket(),
+ SymmetricKeyAlgorithmTags.NULL, null, null,
+ new RSASecretBCPGKey(values[0], values[1], values[2])
+ .getEncoded()),
+ pubKey);
+ } else {
+ throw new PGPException("unknown key type: " + keyType);
+ }
+ }
+
+ throw new PGPException("unknown key type found");
+ }
+
+ /**
+ * Parse a secret key from one of the GPG S expression keys.
+ *
+ * @param inputStream
+ * to read from
+ * @param keyProtectionRemoverFactory
+ * for decrypting encrypted keys
+ * @param fingerPrintCalculator
+ * for calculating key fingerprints
+ *
+ * @return a secret key object.
+ * @throws IOException
+ * @throws PGPException
+ */
+ public PGPSecretKey parseSecretKey(InputStream inputStream,
+ PBEProtectionRemoverFactory keyProtectionRemoverFactory,
+ KeyFingerPrintCalculator fingerPrintCalculator)
+ throws IOException, PGPException {
+ SXprUtils.skipOpenParenthesis(inputStream);
+
+ String type;
+
+ type = SXprUtils.readString(inputStream, inputStream.read());
+ if (type.equals("protected-private-key")
+ || type.equals("private-key")) {
+ SXprUtils.skipOpenParenthesis(inputStream);
+
+ String keyType = SXprUtils.readString(inputStream,
+ inputStream.read());
+ if (keyType.equals("ecc")) {
+ SXprUtils.skipOpenParenthesis(inputStream);
+
+ String curveID = SXprUtils.readString(inputStream,
+ inputStream.read());
+ String curveName = SXprUtils.readString(inputStream,
+ inputStream.read());
+
+ if (curveName.startsWith("NIST ")) {
+ curveName = curveName.substring("NIST ".length());
+ }
+
+ SXprUtils.skipCloseParenthesis(inputStream);
+
+ byte[] qVal;
+
+ SXprUtils.skipOpenParenthesis(inputStream);
+
+ type = SXprUtils.readString(inputStream, inputStream.read());
+ if (type.equals("q")) {
+ qVal = SXprUtils.readBytes(inputStream, inputStream.read());
+ } else {
+ throw new PGPException("no q value found");
+ }
+
+ PublicKeyPacket pubPacket = new PublicKeyPacket(
+ PublicKeyAlgorithmTags.ECDSA, new Date(),
+ new ECDSAPublicBCPGKey(
+ ECNamedCurveTable.getOID(curveName),
+ new BigInteger(1, qVal)));
+
+ SXprUtils.skipCloseParenthesis(inputStream);
+
+ BigInteger d = processECSecretKey(inputStream, curveID,
+ curveName, qVal, keyProtectionRemoverFactory);
+
+ return new PGPSecretKey(
+ new SecretKeyPacket(pubPacket,
+ SymmetricKeyAlgorithmTags.NULL, null, null,
+ new ECSecretBCPGKey(d).getEncoded()),
+ new PGPPublicKey(pubPacket, fingerPrintCalculator));
+ } else if (keyType.equals("dsa")) {
+ BigInteger p = readBigInteger("p", inputStream);
+ BigInteger q = readBigInteger("q", inputStream);
+ BigInteger g = readBigInteger("g", inputStream);
+
+ BigInteger y = readBigInteger("y", inputStream);
+
+ BigInteger x = processDSASecretKey(inputStream, p, q, g, y,
+ keyProtectionRemoverFactory);
+
+ PublicKeyPacket pubPacket = new PublicKeyPacket(
+ PublicKeyAlgorithmTags.DSA, new Date(),
+ new DSAPublicBCPGKey(p, q, g, y));
+
+ return new PGPSecretKey(
+ new SecretKeyPacket(pubPacket,
+ SymmetricKeyAlgorithmTags.NULL, null, null,
+ new DSASecretBCPGKey(x).getEncoded()),
+ new PGPPublicKey(pubPacket, fingerPrintCalculator));
+ } else if (keyType.equals("elg")) {
+ BigInteger p = readBigInteger("p", inputStream);
+ BigInteger g = readBigInteger("g", inputStream);
+
+ BigInteger y = readBigInteger("y", inputStream);
+
+ BigInteger x = processElGamalSecretKey(inputStream, p, g, y,
+ keyProtectionRemoverFactory);
+
+ PublicKeyPacket pubPacket = new PublicKeyPacket(
+ PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, new Date(),
+ new ElGamalPublicBCPGKey(p, g, y));
+
+ return new PGPSecretKey(
+ new SecretKeyPacket(pubPacket,
+ SymmetricKeyAlgorithmTags.NULL, null, null,
+ new ElGamalSecretBCPGKey(x).getEncoded()),
+ new PGPPublicKey(pubPacket, fingerPrintCalculator));
+ } else if (keyType.equals("rsa")) {
+ BigInteger n = readBigInteger("n", inputStream);
+ BigInteger e = readBigInteger("e", inputStream);
+
+ BigInteger[] values = processRSASecretKey(inputStream, n, e,
+ keyProtectionRemoverFactory);
+
+ // TODO: type of RSA key?
+ PublicKeyPacket pubPacket = new PublicKeyPacket(
+ PublicKeyAlgorithmTags.RSA_GENERAL, new Date(),
+ new RSAPublicBCPGKey(n, e));
+
+ return new PGPSecretKey(
+ new SecretKeyPacket(pubPacket,
+ SymmetricKeyAlgorithmTags.NULL, null, null,
+ new RSASecretBCPGKey(values[0], values[1],
+ values[2]).getEncoded()),
+ new PGPPublicKey(pubPacket, fingerPrintCalculator));
+ } else {
+ throw new PGPException("unknown key type: " + keyType);
+ }
+ }
+
+ throw new PGPException("unknown key type found");
+ }
+
+ private BigInteger readBigInteger(String expectedType,
+ InputStream inputStream) throws IOException, PGPException {
+ SXprUtils.skipOpenParenthesis(inputStream);
+
+ String type = SXprUtils.readString(inputStream, inputStream.read());
+ if (!type.equals(expectedType)) {
+ throw new PGPException(expectedType + " value expected");
+ }
+
+ byte[] nBytes = SXprUtils.readBytes(inputStream, inputStream.read());
+ BigInteger v = new BigInteger(1, nBytes);
+
+ SXprUtils.skipCloseParenthesis(inputStream);
+
+ return v;
+ }
+
+ private static byte[][] extractData(InputStream inputStream,
+ PBEProtectionRemoverFactory keyProtectionRemoverFactory)
+ throws PGPException, IOException {
+ byte[] data;
+ byte[] protectedAt = null;
+
+ SXprUtils.skipOpenParenthesis(inputStream);
+
+ String type = SXprUtils.readString(inputStream, inputStream.read());
+ if (type.equals("protected")) {
+ String protection = SXprUtils.readString(inputStream,
+ inputStream.read());
+
+ SXprUtils.skipOpenParenthesis(inputStream);
+
+ S2K s2k = SXprUtils.parseS2K(inputStream);
+
+ byte[] iv = SXprUtils.readBytes(inputStream, inputStream.read());
+
+ SXprUtils.skipCloseParenthesis(inputStream);
+
+ byte[] secKeyData = SXprUtils.readBytes(inputStream,
+ inputStream.read());
+
+ SXprUtils.skipCloseParenthesis(inputStream);
+
+ PBESecretKeyDecryptor keyDecryptor = keyProtectionRemoverFactory
+ .createDecryptor(protection);
+
+ // TODO: recognise other algorithms
+ byte[] key = keyDecryptor.makeKeyFromPassPhrase(
+ SymmetricKeyAlgorithmTags.AES_128, s2k);
+
+ data = keyDecryptor.recoverKeyData(
+ SymmetricKeyAlgorithmTags.AES_128, key, iv, secKeyData, 0,
+ secKeyData.length);
+
+ // check if protected at is present
+ if (inputStream.read() == '(') {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ bOut.write('(');
+ int ch;
+ while ((ch = inputStream.read()) >= 0 && ch != ')') {
+ bOut.write(ch);
+ }
+
+ if (ch != ')') {
+ throw new IOException("unexpected end to SExpr");
+ }
+
+ bOut.write(')');
+
+ protectedAt = bOut.toByteArray();
+ }
+
+ SXprUtils.skipCloseParenthesis(inputStream);
+ SXprUtils.skipCloseParenthesis(inputStream);
+ } else if (type.equals("d") || type.equals("x")) {
+ // JGit modification: unencrypted DSA or ECC keys can have an "x"
+ // here
+ return null;
+ } else {
+ throw new PGPException("protected block not found");
+ }
+
+ return new byte[][] { data, protectedAt };
+ }
+
+ private BigInteger processDSASecretKey(InputStream inputStream,
+ BigInteger p, BigInteger q, BigInteger g, BigInteger y,
+ PBEProtectionRemoverFactory keyProtectionRemoverFactory)
+ throws IOException, PGPException {
+ String type;
+ byte[][] basicData = extractData(inputStream,
+ keyProtectionRemoverFactory);
+
+ // JGit modification: handle unencrypted DSA keys
+ if (basicData == null) {
+ byte[] nBytes = SXprUtils.readBytes(inputStream,
+ inputStream.read());
+ BigInteger x = new BigInteger(1, nBytes);
+ SXprUtils.skipCloseParenthesis(inputStream);
+ return x;
+ }
+
+ byte[] keyData = basicData[0];
+ byte[] protectedAt = basicData[1];
+
+ //
+ // parse the secret key S-expr
+ //
+ InputStream keyIn = new ByteArrayInputStream(keyData);
+
+ SXprUtils.skipOpenParenthesis(keyIn);
+ SXprUtils.skipOpenParenthesis(keyIn);
+
+ BigInteger x = readBigInteger("x", keyIn);
+
+ SXprUtils.skipCloseParenthesis(keyIn);
+
+ // JGit modification: OCB-encrypted keys don't have and don't need a
+ // hash
+ if (keyProtectionRemoverFactory instanceof OCBPBEProtectionRemoverFactory) {
+ return x;
+ }
+
+ SXprUtils.skipOpenParenthesis(keyIn);
+ type = SXprUtils.readString(keyIn, keyIn.read());
+
+ if (!type.equals("hash")) {
+ throw new PGPException("hash keyword expected");
+ }
+ type = SXprUtils.readString(keyIn, keyIn.read());
+
+ if (!type.equals("sha1")) {
+ throw new PGPException("hash keyword expected");
+ }
+
+ byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
+
+ SXprUtils.skipCloseParenthesis(keyIn);
+
+ if (digestProvider != null) {
+ PGPDigestCalculator digestCalculator = digestProvider
+ .get(HashAlgorithmTags.SHA1);
+
+ OutputStream dOut = digestCalculator.getOutputStream();
+
+ dOut.write(Strings.toByteArray("(3:dsa"));
+ writeCanonical(dOut, "p", p);
+ writeCanonical(dOut, "q", q);
+ writeCanonical(dOut, "g", g);
+ writeCanonical(dOut, "y", y);
+ writeCanonical(dOut, "x", x);
+
+ // check protected-at
+ if (protectedAt != null) {
+ dOut.write(protectedAt);
+ }
+
+ dOut.write(Strings.toByteArray(")"));
+
+ byte[] check = digestCalculator.getDigest();
+ if (!Arrays.constantTimeAreEqual(check, hashBytes)) {
+ throw new PGPException(
+ "checksum on protected data failed in SExpr");
+ }
+ }
+
+ return x;
+ }
+
+ private BigInteger processElGamalSecretKey(InputStream inputStream,
+ BigInteger p, BigInteger g, BigInteger y,
+ PBEProtectionRemoverFactory keyProtectionRemoverFactory)
+ throws IOException, PGPException {
+ String type;
+ byte[][] basicData = extractData(inputStream,
+ keyProtectionRemoverFactory);
+
+ // JGit modification: handle unencrypted EC keys
+ if (basicData == null) {
+ byte[] nBytes = SXprUtils.readBytes(inputStream,
+ inputStream.read());
+ BigInteger x = new BigInteger(1, nBytes);
+ SXprUtils.skipCloseParenthesis(inputStream);
+ return x;
+ }
+
+ byte[] keyData = basicData[0];
+ byte[] protectedAt = basicData[1];
+
+ //
+ // parse the secret key S-expr
+ //
+ InputStream keyIn = new ByteArrayInputStream(keyData);
+
+ SXprUtils.skipOpenParenthesis(keyIn);
+ SXprUtils.skipOpenParenthesis(keyIn);
+
+ BigInteger x = readBigInteger("x", keyIn);
+
+ SXprUtils.skipCloseParenthesis(keyIn);
+
+ // JGit modification: OCB-encrypted keys don't have and don't need a
+ // hash
+ if (keyProtectionRemoverFactory instanceof OCBPBEProtectionRemoverFactory) {
+ return x;
+ }
+
+ SXprUtils.skipOpenParenthesis(keyIn);
+ type = SXprUtils.readString(keyIn, keyIn.read());
+
+ if (!type.equals("hash")) {
+ throw new PGPException("hash keyword expected");
+ }
+ type = SXprUtils.readString(keyIn, keyIn.read());
+
+ if (!type.equals("sha1")) {
+ throw new PGPException("hash keyword expected");
+ }
+
+ byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
+
+ SXprUtils.skipCloseParenthesis(keyIn);
+
+ if (digestProvider != null) {
+ PGPDigestCalculator digestCalculator = digestProvider
+ .get(HashAlgorithmTags.SHA1);
+
+ OutputStream dOut = digestCalculator.getOutputStream();
+
+ dOut.write(Strings.toByteArray("(3:elg"));
+ writeCanonical(dOut, "p", p);
+ writeCanonical(dOut, "g", g);
+ writeCanonical(dOut, "y", y);
+ writeCanonical(dOut, "x", x);
+
+ // check protected-at
+ if (protectedAt != null) {
+ dOut.write(protectedAt);
+ }
+
+ dOut.write(Strings.toByteArray(")"));
+
+ byte[] check = digestCalculator.getDigest();
+ if (!Arrays.constantTimeAreEqual(check, hashBytes)) {
+ throw new PGPException(
+ "checksum on protected data failed in SExpr");
+ }
+ }
+
+ return x;
+ }
+
+ private BigInteger processECSecretKey(InputStream inputStream,
+ String curveID, String curveName, byte[] qVal,
+ PBEProtectionRemoverFactory keyProtectionRemoverFactory)
+ throws IOException, PGPException {
+ String type;
+
+ byte[][] basicData = extractData(inputStream,
+ keyProtectionRemoverFactory);
+
+ // JGit modification: handle unencrypted EC keys
+ if (basicData == null) {
+ byte[] nBytes = SXprUtils.readBytes(inputStream,
+ inputStream.read());
+ BigInteger d = new BigInteger(1, nBytes);
+ SXprUtils.skipCloseParenthesis(inputStream);
+ return d;
+ }
+
+ byte[] keyData = basicData[0];
+ byte[] protectedAt = basicData[1];
+
+ //
+ // parse the secret key S-expr
+ //
+ InputStream keyIn = new ByteArrayInputStream(keyData);
+
+ SXprUtils.skipOpenParenthesis(keyIn);
+ SXprUtils.skipOpenParenthesis(keyIn);
+ BigInteger d = readBigInteger("d", keyIn);
+ SXprUtils.skipCloseParenthesis(keyIn);
+
+ // JGit modification: OCB-encrypted keys don't have and don't need a
+ // hash
+ if (keyProtectionRemoverFactory instanceof OCBPBEProtectionRemoverFactory) {
+ return d;
+ }
+
+ SXprUtils.skipOpenParenthesis(keyIn);
+
+ type = SXprUtils.readString(keyIn, keyIn.read());
+
+ if (!type.equals("hash")) {
+ throw new PGPException("hash keyword expected");
+ }
+ type = SXprUtils.readString(keyIn, keyIn.read());
+
+ if (!type.equals("sha1")) {
+ throw new PGPException("hash keyword expected");
+ }
+
+ byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
+
+ SXprUtils.skipCloseParenthesis(keyIn);
+
+ if (digestProvider != null) {
+ PGPDigestCalculator digestCalculator = digestProvider
+ .get(HashAlgorithmTags.SHA1);
+
+ OutputStream dOut = digestCalculator.getOutputStream();
+
+ dOut.write(Strings.toByteArray("(3:ecc"));
+
+ dOut.write(Strings.toByteArray("(" + curveID.length() + ":"
+ + curveID + curveName.length() + ":" + curveName + ")"));
+
+ writeCanonical(dOut, "q", qVal);
+ writeCanonical(dOut, "d", d);
+
+ // check protected-at
+ if (protectedAt != null) {
+ dOut.write(protectedAt);
+ }
+
+ dOut.write(Strings.toByteArray(")"));
+
+ byte[] check = digestCalculator.getDigest();
+
+ if (!Arrays.constantTimeAreEqual(check, hashBytes)) {
+ throw new PGPException(
+ "checksum on protected data failed in SExpr");
+ }
+ }
+
+ return d;
+ }
+
+ private BigInteger[] processRSASecretKey(InputStream inputStream,
+ BigInteger n, BigInteger e,
+ PBEProtectionRemoverFactory keyProtectionRemoverFactory)
+ throws IOException, PGPException {
+ String type;
+ byte[][] basicData = extractData(inputStream,
+ keyProtectionRemoverFactory);
+
+ byte[] keyData;
+ byte[] protectedAt = null;
+
+ InputStream keyIn;
+ BigInteger d;
+
+ if (basicData == null) {
+ keyIn = inputStream;
+ byte[] nBytes = SXprUtils.readBytes(inputStream,
+ inputStream.read());
+ d = new BigInteger(1, nBytes);
+
+ SXprUtils.skipCloseParenthesis(inputStream);
+
+ } else {
+ keyData = basicData[0];
+ protectedAt = basicData[1];
+
+ keyIn = new ByteArrayInputStream(keyData);
+
+ SXprUtils.skipOpenParenthesis(keyIn);
+ SXprUtils.skipOpenParenthesis(keyIn);
+ d = readBigInteger("d", keyIn);
+ }
+
+ //
+ // parse the secret key S-expr
+ //
+
+ BigInteger p = readBigInteger("p", keyIn);
+ BigInteger q = readBigInteger("q", keyIn);
+ BigInteger u = readBigInteger("u", keyIn);
+
+ // JGit modification: OCB-encrypted keys don't have and don't need a
+ // hash
+ if (basicData == null
+ || keyProtectionRemoverFactory instanceof OCBPBEProtectionRemoverFactory) {
+ return new BigInteger[] { d, p, q, u };
+ }
+
+ SXprUtils.skipCloseParenthesis(keyIn);
+
+ SXprUtils.skipOpenParenthesis(keyIn);
+ type = SXprUtils.readString(keyIn, keyIn.read());
+
+ if (!type.equals("hash")) {
+ throw new PGPException("hash keyword expected");
+ }
+ type = SXprUtils.readString(keyIn, keyIn.read());
+
+ if (!type.equals("sha1")) {
+ throw new PGPException("hash keyword expected");
+ }
+
+ byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
+
+ SXprUtils.skipCloseParenthesis(keyIn);
+
+ if (digestProvider != null) {
+ PGPDigestCalculator digestCalculator = digestProvider
+ .get(HashAlgorithmTags.SHA1);
+
+ OutputStream dOut = digestCalculator.getOutputStream();
+
+ dOut.write(Strings.toByteArray("(3:rsa"));
+
+ writeCanonical(dOut, "n", n);
+ writeCanonical(dOut, "e", e);
+ writeCanonical(dOut, "d", d);
+ writeCanonical(dOut, "p", p);
+ writeCanonical(dOut, "q", q);
+ writeCanonical(dOut, "u", u);
+
+ // check protected-at
+ if (protectedAt != null) {
+ dOut.write(protectedAt);
+ }
+
+ dOut.write(Strings.toByteArray(")"));
+
+ byte[] check = digestCalculator.getDigest();
+
+ if (!Arrays.constantTimeAreEqual(check, hashBytes)) {
+ throw new PGPException(
+ "checksum on protected data failed in SExpr");
+ }
+ }
+
+ return new BigInteger[] { d, p, q, u };
+ }
+
+ private void writeCanonical(OutputStream dOut, String label, BigInteger i)
+ throws IOException {
+ writeCanonical(dOut, label, i.toByteArray());
+ }
+
+ private void writeCanonical(OutputStream dOut, String label, byte[] data)
+ throws IOException {
+ dOut.write(Strings.toByteArray(
+ "(" + label.length() + ":" + label + data.length + ":"));
+ dOut.write(data);
+ dOut.write(Strings.toByteArray(")"));
+ }
+}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SXprUtils.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SXprUtils.java
new file mode 100644
index 0000000000..220aa285ff
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SXprUtils.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org)
+ * <p>
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ *including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * </p>
+ * <p>
+ * The above copyright notice and this permission notice shall be included in all copies or substantial
+ * portions of the Software.
+ * </p>
+ * <p>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * </p>
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+// This class is an unmodified copy from Bouncy Castle; needed because it's package-visible only and used by SExprParser.
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.bcpg.HashAlgorithmTags;
+import org.bouncycastle.bcpg.S2K;
+import org.bouncycastle.util.io.Streams;
+
+/**
+ * Utility functions for looking a S-expression keys. This class will move when
+ * it finds a better home!
+ * <p>
+ * Format documented here:
+ * http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=agent/keyformat.txt;h=42c4b1f06faf1bbe71ffadc2fee0fad6bec91a97;hb=refs/heads/master
+ * </p>
+ */
+class SXprUtils {
+ private static int readLength(InputStream in, int ch) throws IOException {
+ int len = ch - '0';
+
+ while ((ch = in.read()) >= 0 && ch != ':') {
+ len = len * 10 + ch - '0';
+ }
+
+ return len;
+ }
+
+ static String readString(InputStream in, int ch) throws IOException {
+ int len = readLength(in, ch);
+
+ char[] chars = new char[len];
+
+ for (int i = 0; i != chars.length; i++) {
+ chars[i] = (char) in.read();
+ }
+
+ return new String(chars);
+ }
+
+ static byte[] readBytes(InputStream in, int ch) throws IOException {
+ int len = readLength(in, ch);
+
+ byte[] data = new byte[len];
+
+ Streams.readFully(in, data);
+
+ return data;
+ }
+
+ static S2K parseS2K(InputStream in) throws IOException {
+ skipOpenParenthesis(in);
+
+ // Algorithm is hard-coded to SHA1 below anyway.
+ readString(in, in.read());
+ byte[] iv = readBytes(in, in.read());
+ final long iterationCount = Long.parseLong(readString(in, in.read()));
+
+ skipCloseParenthesis(in);
+
+ // we have to return the actual iteration count provided.
+ S2K s2k = new S2K(HashAlgorithmTags.SHA1, iv, (int) iterationCount) {
+ @Override
+ public long getIterationCount() {
+ return iterationCount;
+ }
+ };
+
+ return s2k;
+ }
+
+ static void skipOpenParenthesis(InputStream in) throws IOException {
+ int ch = in.read();
+ if (ch != '(') {
+ throw new IOException(
+ "unknown character encountered: " + (char) ch); //$NON-NLS-1$
+ }
+ }
+
+ static void skipCloseParenthesis(InputStream in) throws IOException {
+ int ch = in.read();
+ if (ch != ')') {
+ throw new IOException("unknown character encountered"); //$NON-NLS-1$
+ }
+ }
+}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeys.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeys.java
new file mode 100644
index 0000000000..269a1ba0f6
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeys.java
@@ -0,0 +1,597 @@
+/*
+ * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.Arrays;
+
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPSecretKey;
+import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
+import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
+import org.bouncycastle.openpgp.operator.jcajce.JcePBEProtectionRemoverFactory;
+import org.bouncycastle.util.io.Streams;
+import org.eclipse.jgit.api.errors.CanceledException;
+import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.gpg.bc.internal.BCText;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * Utilities for reading GPG secret keys from a gpg-agent key file.
+ */
+public final class SecretKeys {
+
+ private SecretKeys() {
+ // No instantiation.
+ }
+
+ /**
+ * Something that can supply a passphrase to decrypt an encrypted secret
+ * key.
+ */
+ public interface PassphraseSupplier {
+
+ /**
+ * Supplies a passphrase.
+ *
+ * @return the passphrase
+ * @throws PGPException
+ * if no passphrase can be obtained
+ * @throws CanceledException
+ * if the user canceled passphrase entry
+ * @throws UnsupportedCredentialItem
+ * if an internal error occurred
+ * @throws URISyntaxException
+ * if an internal error occurred
+ */
+ char[] getPassphrase() throws PGPException, CanceledException,
+ UnsupportedCredentialItem, URISyntaxException;
+ }
+
+ private static final byte[] PROTECTED_KEY = "protected-private-key" //$NON-NLS-1$
+ .getBytes(StandardCharsets.US_ASCII);
+
+ private static final byte[] OCB_PROTECTED = "openpgp-s2k3-ocb-aes" //$NON-NLS-1$
+ .getBytes(StandardCharsets.US_ASCII);
+
+ /**
+ * Reads a GPG secret key from the given stream.
+ *
+ * @param in
+ * {@link InputStream} to read from, doesn't need to be buffered
+ * @param calculatorProvider
+ * for checking digests
+ * @param passphraseSupplier
+ * for decrypting encrypted keys
+ * @param publicKey
+ * the secret key should be for
+ * @return the secret key
+ * @throws IOException
+ * if the stream cannot be parsed
+ * @throws PGPException
+ * if thrown by the underlying S-Expression parser, for instance
+ * when the passphrase is wrong
+ * @throws CanceledException
+ * if thrown by the {@code passphraseSupplier}
+ * @throws UnsupportedCredentialItem
+ * if thrown by the {@code passphraseSupplier}
+ * @throws URISyntaxException
+ * if thrown by the {@code passphraseSupplier}
+ */
+ public static PGPSecretKey readSecretKey(InputStream in,
+ PGPDigestCalculatorProvider calculatorProvider,
+ PassphraseSupplier passphraseSupplier, PGPPublicKey publicKey)
+ throws IOException, PGPException, CanceledException,
+ UnsupportedCredentialItem, URISyntaxException {
+ byte[] data = Streams.readAll(in);
+ if (data.length == 0) {
+ throw new EOFException();
+ } else if (data.length < 4 + PROTECTED_KEY.length) {
+ // +4 for "(21:" for a binary protected key
+ throw new IOException(
+ MessageFormat.format(BCText.get().secretKeyTooShort,
+ Integer.toUnsignedString(data.length)));
+ }
+ SExprParser parser = new SExprParser(calculatorProvider);
+ byte firstChar = data[0];
+ try {
+ if (firstChar == '(') {
+ // Binary format.
+ PBEProtectionRemoverFactory decryptor = null;
+ if (matches(data, 4, PROTECTED_KEY)) {
+ // AES/CBC encrypted.
+ decryptor = new JcePBEProtectionRemoverFactory(
+ passphraseSupplier.getPassphrase(),
+ calculatorProvider);
+ }
+ try (InputStream sIn = new ByteArrayInputStream(data)) {
+ return parser.parseSecretKey(sIn, decryptor, publicKey);
+ }
+ }
+ // Assume it's the new key-value format.
+ try (ByteArrayInputStream keyIn = new ByteArrayInputStream(data)) {
+ byte[] rawData = keyFromNameValueFormat(keyIn);
+ if (!matches(rawData, 1, PROTECTED_KEY)) {
+ // Not encrypted human-readable format.
+ try (InputStream sIn = new ByteArrayInputStream(
+ convertSexpression(rawData))) {
+ return parser.parseSecretKey(sIn, null, publicKey);
+ }
+ }
+ // An encrypted key from a key-value file. Most likely AES/OCB
+ // encrypted.
+ boolean isOCB[] = { false };
+ byte[] sExp = convertSexpression(rawData, isOCB);
+ PBEProtectionRemoverFactory decryptor;
+ if (isOCB[0]) {
+ decryptor = new OCBPBEProtectionRemoverFactory(
+ passphraseSupplier.getPassphrase(),
+ calculatorProvider, getAad(sExp));
+ } else {
+ decryptor = new JcePBEProtectionRemoverFactory(
+ passphraseSupplier.getPassphrase(),
+ calculatorProvider);
+ }
+ try (InputStream sIn = new ByteArrayInputStream(sExp)) {
+ return parser.parseSecretKey(sIn, decryptor, publicKey);
+ }
+ }
+ } catch (IOException e) {
+ throw new PGPException(e.getLocalizedMessage(), e);
+ }
+ }
+
+ /**
+ * Extract the AAD for the OCB decryption from an s-expression.
+ *
+ * @param sExp
+ * buffer containing a valid binary s-expression
+ * @return the AAD
+ */
+ private static byte[] getAad(byte[] sExp) {
+ // Given a key
+ // @formatter:off
+ // (protected-private-key (rsa ... (protected openpgp-s2k3-ocb-aes ... )(protected-at ...)))
+ // A B C D
+ // The AAD is [A..B)[C..D). (From the binary serialized form.)
+ // @formatter:on
+ int i = 1; // Skip initial '('
+ while (sExp[i] != '(') {
+ i++;
+ }
+ int aadStart = i++;
+ int aadEnd = skip(sExp, aadStart);
+ byte[] protectedPrefix = "(9:protected" //$NON-NLS-1$
+ .getBytes(StandardCharsets.US_ASCII);
+ while (!matches(sExp, i, protectedPrefix)) {
+ i++;
+ }
+ int protectedStart = i;
+ int protectedEnd = skip(sExp, protectedStart);
+ byte[] aadData = new byte[aadEnd - aadStart
+ - (protectedEnd - protectedStart)];
+ System.arraycopy(sExp, aadStart, aadData, 0, protectedStart - aadStart);
+ System.arraycopy(sExp, protectedEnd, aadData, protectedStart - aadStart,
+ aadEnd - protectedEnd);
+ return aadData;
+ }
+
+ /**
+ * Skips a list including nested lists.
+ *
+ * @param sExp
+ * buffer containing valid binary s-expression data
+ * @param start
+ * index of the opening '(' of the list to skip
+ * @return the index after the closing ')' of the skipped list
+ */
+ private static int skip(byte[] sExp, int start) {
+ int i = start + 1;
+ int depth = 1;
+ while (depth > 0) {
+ switch (sExp[i]) {
+ case '(':
+ depth++;
+ break;
+ case ')':
+ depth--;
+ break;
+ default:
+ // We must be on a length
+ int j = i;
+ while (sExp[j] >= '0' && sExp[j] <= '9') {
+ j++;
+ }
+ // j is on the colon
+ int length = Integer.parseInt(
+ new String(sExp, i, j - i, StandardCharsets.US_ASCII));
+ i = j + length;
+ }
+ i++;
+ }
+ return i;
+ }
+
+ /**
+ * Checks whether the {@code needle} matches {@code src} at offset
+ * {@code from}.
+ *
+ * @param src
+ * to match against {@code needle}
+ * @param from
+ * position in {@code src} to start matching
+ * @param needle
+ * to match against
+ * @return {@code true} if {@code src} contains {@code needle} at position
+ * {@code from}, {@code false} otherwise
+ */
+ private static boolean matches(byte[] src, int from, byte[] needle) {
+ if (from < 0 || from + needle.length > src.length) {
+ return false;
+ }
+ return org.bouncycastle.util.Arrays.constantTimeAreEqual(needle.length,
+ src, from, needle, 0);
+ }
+
+ /**
+ * Converts a human-readable serialized s-expression into a binary
+ * serialized s-expression.
+ *
+ * @param humanForm
+ * to convert
+ * @return the converted s-expression
+ * @throws IOException
+ * if the conversion fails
+ */
+ private static byte[] convertSexpression(byte[] humanForm)
+ throws IOException {
+ boolean[] isOCB = { false };
+ return convertSexpression(humanForm, isOCB);
+ }
+
+ /**
+ * Converts a human-readable serialized s-expression into a binary
+ * serialized s-expression.
+ *
+ * @param humanForm
+ * to convert
+ * @param isOCB
+ * returns whether the s-expression specified AES/OCB encryption
+ * @return the converted s-expression
+ * @throws IOException
+ * if the conversion fails
+ */
+ private static byte[] convertSexpression(byte[] humanForm, boolean[] isOCB)
+ throws IOException {
+ int pos = 0;
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream(
+ humanForm.length)) {
+ while (pos < humanForm.length) {
+ byte b = humanForm[pos];
+ if (b == '(' || b == ')') {
+ out.write(b);
+ pos++;
+ } else if (isGpgSpace(b)) {
+ pos++;
+ } else if (b == '#') {
+ // Hex value follows up to the next #
+ int i = ++pos;
+ while (i < humanForm.length && isHex(humanForm[i])) {
+ i++;
+ }
+ if (i == pos || humanForm[i] != '#') {
+ throw new StreamCorruptedException(
+ BCText.get().sexprHexNotClosed);
+ }
+ if ((i - pos) % 2 != 0) {
+ throw new StreamCorruptedException(
+ BCText.get().sexprHexOdd);
+ }
+ int l = (i - pos) / 2;
+ out.write(Integer.toString(l)
+ .getBytes(StandardCharsets.US_ASCII));
+ out.write(':');
+ while (pos < i) {
+ int x = (nibble(humanForm[pos]) << 4)
+ | nibble(humanForm[pos + 1]);
+ pos += 2;
+ out.write(x);
+ }
+ pos = i + 1;
+ } else if (isTokenChar(b)) {
+ // Scan the token
+ int start = pos++;
+ while (pos < humanForm.length
+ && isTokenChar(humanForm[pos])) {
+ pos++;
+ }
+ int l = pos - start;
+ if (pos - start == OCB_PROTECTED.length
+ && matches(humanForm, start, OCB_PROTECTED)) {
+ isOCB[0] = true;
+ }
+ out.write(Integer.toString(l)
+ .getBytes(StandardCharsets.US_ASCII));
+ out.write(':');
+ out.write(humanForm, start, pos - start);
+ } else if (b == '"') {
+ // Potentially quoted string.
+ int start = ++pos;
+ boolean escaped = false;
+ while (pos < humanForm.length
+ && (escaped || humanForm[pos] != '"')) {
+ int ch = humanForm[pos++];
+ escaped = !escaped && ch == '\\';
+ }
+ if (pos >= humanForm.length) {
+ throw new StreamCorruptedException(
+ BCText.get().sexprStringNotClosed);
+ }
+ // start is on the first character of the string, pos on the
+ // closing quote.
+ byte[] dq = dequote(humanForm, start, pos);
+ out.write(Integer.toString(dq.length)
+ .getBytes(StandardCharsets.US_ASCII));
+ out.write(':');
+ out.write(dq);
+ pos++;
+ } else {
+ throw new StreamCorruptedException(
+ MessageFormat.format(BCText.get().sexprUnhandled,
+ Integer.toHexString(b & 0xFF)));
+ }
+ }
+ return out.toByteArray();
+ }
+ }
+
+ /**
+ * GPG-style string de-quoting, which is basically C-style, with some
+ * literal CR/LF escaping.
+ *
+ * @param in
+ * buffer containing the quoted string
+ * @param from
+ * index after the opening quote in {@code in}
+ * @param to
+ * index of the closing quote in {@code in}
+ * @return the dequoted raw string value
+ * @throws StreamCorruptedException
+ */
+ private static byte[] dequote(byte[] in, int from, int to)
+ throws StreamCorruptedException {
+ // Result must be shorter or have the same length
+ byte[] out = new byte[to - from];
+ int j = 0;
+ int i = from;
+ while (i < to) {
+ byte b = in[i++];
+ if (b != '\\') {
+ out[j++] = b;
+ continue;
+ }
+ if (i == to) {
+ throw new StreamCorruptedException(
+ BCText.get().sexprStringInvalidEscapeAtEnd);
+ }
+ b = in[i++];
+ switch (b) {
+ case 'b':
+ out[j++] = '\b';
+ break;
+ case 'f':
+ out[j++] = '\f';
+ break;
+ case 'n':
+ out[j++] = '\n';
+ break;
+ case 'r':
+ out[j++] = '\r';
+ break;
+ case 't':
+ out[j++] = '\t';
+ break;
+ case 'v':
+ out[j++] = 0x0B;
+ break;
+ case '"':
+ case '\'':
+ case '\\':
+ out[j++] = b;
+ break;
+ case '\r':
+ // Escaped literal line end. If an LF is following, skip that,
+ // too.
+ if (i < to && in[i] == '\n') {
+ i++;
+ }
+ break;
+ case '\n':
+ // Same for LF possibly followed by CR.
+ if (i < to && in[i] == '\r') {
+ i++;
+ }
+ break;
+ case 'x':
+ if (i + 1 >= to || !isHex(in[i]) || !isHex(in[i + 1])) {
+ throw new StreamCorruptedException(
+ BCText.get().sexprStringInvalidHexEscape);
+ }
+ out[j++] = (byte) ((nibble(in[i]) << 4) | nibble(in[i + 1]));
+ i += 2;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ if (i + 2 >= to || !isOctal(in[i]) || !isOctal(in[i + 1])
+ || !isOctal(in[i + 2])) {
+ throw new StreamCorruptedException(
+ BCText.get().sexprStringInvalidOctalEscape);
+ }
+ out[j++] = (byte) (((((in[i] - '0') << 3)
+ | (in[i + 1] - '0')) << 3) | (in[i + 2] - '0'));
+ i += 3;
+ break;
+ default:
+ throw new StreamCorruptedException(MessageFormat.format(
+ BCText.get().sexprStringInvalidEscape,
+ Integer.toHexString(b & 0xFF)));
+ }
+ }
+ return Arrays.copyOf(out, j);
+ }
+
+ /**
+ * Extracts the key from a GPG name-value-pair key file.
+ * <p>
+ * Package-visible for tests only.
+ * </p>
+ *
+ * @param in
+ * {@link InputStream} to read from; should be buffered
+ * @return the raw key data as extracted from the file
+ * @throws IOException
+ * if the {@code in} stream cannot be read or does not contain a
+ * key
+ */
+ static byte[] keyFromNameValueFormat(InputStream in) throws IOException {
+ // It would be nice if we could use RawParseUtils here, but GPG compares
+ // names case-insensitively. We're only interested in the "Key:"
+ // name-value pair.
+ int[] nameLow = { 'k', 'e', 'y', ':' };
+ int[] nameCap = { 'K', 'E', 'Y', ':' };
+ int nameIdx = 0;
+ for (;;) {
+ int next = in.read();
+ if (next < 0) {
+ throw new EOFException();
+ }
+ if (next == '\n') {
+ nameIdx = 0;
+ } else if (nameIdx >= 0) {
+ if (nameLow[nameIdx] == next || nameCap[nameIdx] == next) {
+ nameIdx++;
+ if (nameIdx == nameLow.length) {
+ break;
+ }
+ } else {
+ nameIdx = -1;
+ }
+ }
+ }
+ // We're after "Key:". Read the value as continuation lines.
+ int last = ':';
+ byte[] rawData;
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream(8192)) {
+ for (;;) {
+ int next = in.read();
+ if (next < 0) {
+ break;
+ }
+ if (last == '\n') {
+ if (next == ' ' || next == '\t') {
+ // Continuation line; skip this whitespace
+ last = next;
+ continue;
+ }
+ break; // Not a continuation line
+ }
+ out.write(next);
+ last = next;
+ }
+ rawData = out.toByteArray();
+ }
+ // GPG trims off trailing whitespace, and a line having only whitespace
+ // is a single LF.
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream(
+ rawData.length)) {
+ int lineStart = 0;
+ boolean trimLeading = true;
+ while (lineStart < rawData.length) {
+ int nextLineStart = RawParseUtils.nextLF(rawData, lineStart);
+ if (trimLeading) {
+ while (lineStart < nextLineStart
+ && isGpgSpace(rawData[lineStart])) {
+ lineStart++;
+ }
+ }
+ // Trim trailing
+ int i = nextLineStart - 1;
+ while (lineStart < i && isGpgSpace(rawData[i])) {
+ i--;
+ }
+ if (i <= lineStart) {
+ // Empty line signifies LF
+ out.write('\n');
+ trimLeading = true;
+ } else {
+ out.write(rawData, lineStart, i - lineStart + 1);
+ trimLeading = false;
+ }
+ lineStart = nextLineStart;
+ }
+ return out.toByteArray();
+ }
+ }
+
+ private static boolean isGpgSpace(int ch) {
+ return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
+ }
+
+ private static boolean isTokenChar(int ch) {
+ switch (ch) {
+ case '-':
+ case '.':
+ case '/':
+ case '_':
+ case ':':
+ case '*':
+ case '+':
+ case '=':
+ return true;
+ default:
+ if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
+ || (ch >= '0' && ch <= '9')) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private static boolean isHex(int ch) {
+ return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')
+ || (ch >= 'a' && ch <= 'f');
+ }
+
+ private static boolean isOctal(int ch) {
+ return (ch >= '0' && ch <= '7');
+ }
+
+ private static int nibble(int ch) {
+ if (ch >= '0' && ch <= '9') {
+ return ch - '0';
+ } else if (ch >= 'A' && ch <= 'F') {
+ return ch - 'A' + 10;
+ } else if (ch >= 'a' && ch <= 'f') {
+ return ch - 'a' + 10;
+ }
+ return -1;
+ }
+}
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index 0fef0cfbd1..0f2d7a3b63 100644
--- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
@@ -9,20 +9,20 @@ Bundle-Localization: plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
Import-Package: org.apache.http;version="[4.3.0,5.0.0)",
- org.apache.http.client;version="[4.3.0,5.0.0)",
- org.apache.http.client.config;version="[4.3.0,5.0.0)",
- org.apache.http.client.methods;version="[4.3.0,5.0.0)",
- org.apache.http.client.params;version="[4.3.0,5.0.0)",
+ org.apache.http.client;version="[4.4.0,5.0.0)",
+ org.apache.http.client.config;version="[4.4.0,5.0.0)",
+ org.apache.http.client.methods;version="[4.4.0,5.0.0)",
+ org.apache.http.client.params;version="[4.4.0,5.0.0)",
org.apache.http.config;version="[4.3.0,5.0.0)",
- org.apache.http.conn;version="[4.3.0,5.0.0)",
- org.apache.http.conn.params;version="[4.3.0,5.0.0)",
- org.apache.http.conn.scheme;version="[4.3.0,5.0.0)",
- org.apache.http.conn.socket;version="[4.3.0,5.0.0)",
- org.apache.http.conn.ssl;version="[4.3.0,5.0.0)",
- org.apache.http.conn.util;version="[4.3.0,5.0.0)",
+ org.apache.http.conn;version="[4.4.0,5.0.0)",
+ org.apache.http.conn.params;version="[4.4.0,5.0.0)",
+ org.apache.http.conn.scheme;version="[4.4.0,5.0.0)",
+ org.apache.http.conn.socket;version="[4.4.0,5.0.0)",
+ org.apache.http.conn.ssl;version="[4.4.0,5.0.0)",
+ org.apache.http.conn.util;version="[4.4.0,5.0.0)",
org.apache.http.entity;version="[4.3.0,5.0.0)",
- org.apache.http.impl.client;version="[4.3.0,5.0.0)",
- org.apache.http.impl.conn;version="[4.3.0,5.0.0)",
+ org.apache.http.impl.client;version="[4.4.0,5.0.0)",
+ org.apache.http.impl.conn;version="[4.4.0,5.0.0)",
org.apache.http.params;version="[4.3.0,5.0.0)",
org.apache.http.ssl;version="[4.3.0,5.0.0)",
org.eclipse.jgit.annotations;version="[6.0.0,6.1.0)",
diff --git a/org.eclipse.jgit.http.apache/resources/org/eclipse/jgit/transport/http/apache/internal/HttpApacheText.properties b/org.eclipse.jgit.http.apache/resources/org/eclipse/jgit/transport/http/apache/internal/HttpApacheText.properties
index d2e5216989..b7b9af0a4a 100644
--- a/org.eclipse.jgit.http.apache/resources/org/eclipse/jgit/transport/http/apache/internal/HttpApacheText.properties
+++ b/org.eclipse.jgit.http.apache/resources/org/eclipse/jgit/transport/http/apache/internal/HttpApacheText.properties
@@ -1 +1,2 @@
+httpWrongConnectionType=Wrong connection type: expected {0}, got {1}.
unexpectedSSLContextException=unexpected exception when searching for the TLS protocol
diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
index ed05f0a8d8..90348f54b9 100644
--- a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
+++ b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
@@ -57,9 +57,7 @@ import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
-import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.conn.util.PublicSuffixMatcherLoader;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.SystemDefaultCredentialsProvider;
@@ -103,7 +101,11 @@ public class HttpClientConnection implements HttpConnection {
private HostnameVerifier hostnameverifier;
- SSLContext ctx;
+ private SSLContext ctx;
+
+ private SSLConnectionSocketFactory socketFactory;
+
+ private boolean usePooling = true;
private HttpClient getClient() {
if (client == null) {
@@ -125,11 +127,18 @@ public class HttpClientConnection implements HttpConnection {
configBuilder
.setRedirectsEnabled(followRedirects.booleanValue());
}
- SSLConnectionSocketFactory sslConnectionFactory = getSSLSocketFactory();
+ boolean pooled = true;
+ SSLConnectionSocketFactory sslConnectionFactory;
+ if (socketFactory != null) {
+ pooled = usePooling;
+ sslConnectionFactory = socketFactory;
+ } else {
+ // Legacy implementation.
+ pooled = (hostnameverifier == null);
+ sslConnectionFactory = getSSLSocketFactory();
+ }
clientBuilder.setSSLSocketFactory(sslConnectionFactory);
- if (hostnameverifier != null) {
- // Using a custom verifier: we don't want pooled connections
- // with this.
+ if (!pooled) {
Registry<ConnectionSocketFactory> registry = RegistryBuilder
.<ConnectionSocketFactory> create()
.register("https", sslConnectionFactory)
@@ -147,14 +156,19 @@ public class HttpClientConnection implements HttpConnection {
return client;
}
+ void setSSLSocketFactory(@NonNull SSLConnectionSocketFactory factory,
+ boolean isDefault) {
+ socketFactory = factory;
+ usePooling = isDefault;
+ }
+
private SSLConnectionSocketFactory getSSLSocketFactory() {
HostnameVerifier verifier = hostnameverifier;
SSLContext context;
if (verifier == null) {
// Use defaults
- context = SSLContexts.createDefault();
- verifier = new DefaultHostnameVerifier(
- PublicSuffixMatcherLoader.getDefault());
+ context = SSLContexts.createSystemDefault();
+ verifier = SSLConnectionSocketFactory.getDefaultHostnameVerifier();
} else {
// Using a custom verifier. Attention: configure() must have been
// called already, otherwise one gets a "context not initialized"
diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java
index 3c05cdef8c..4de3e470f6 100644
--- a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java
+++ b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Christian Halstrick <christian.halstrick@sap.com> and others
+ * Copyright (C) 2013, 2020 Christian Halstrick <christian.halstrick@sap.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -12,27 +12,100 @@ package org.eclipse.jgit.transport.http.apache;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.text.MessageFormat;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.eclipse.jgit.transport.http.HttpConnection;
-import org.eclipse.jgit.transport.http.HttpConnectionFactory;
+import org.eclipse.jgit.transport.http.HttpConnectionFactory2;
+import org.eclipse.jgit.transport.http.NoCheckX509TrustManager;
+import org.eclipse.jgit.transport.http.apache.internal.HttpApacheText;
+import org.eclipse.jgit.util.HttpSupport;
/**
- * A factory returning instances of
- * {@link org.eclipse.jgit.transport.http.apache.HttpClientConnection}
+ * A factory returning instances of {@link HttpClientConnection}.
*
* @since 3.3
*/
-public class HttpClientConnectionFactory implements HttpConnectionFactory {
- /** {@inheritDoc} */
+public class HttpClientConnectionFactory implements HttpConnectionFactory2 {
+
@Override
public HttpConnection create(URL url) throws IOException {
return new HttpClientConnection(url.toString());
}
- /** {@inheritDoc} */
@Override
- public HttpConnection create(URL url, Proxy proxy)
- throws IOException {
+ public HttpConnection create(URL url, Proxy proxy) throws IOException {
return new HttpClientConnection(url.toString(), proxy);
}
+
+ @Override
+ public GitSession newSession() {
+ return new HttpClientSession();
+ }
+
+ private static class HttpClientSession implements GitSession {
+
+ private SSLContext securityContext;
+
+ private SSLConnectionSocketFactory socketFactory;
+
+ private boolean isDefault;
+
+ @Override
+ public HttpClientConnection configure(HttpConnection connection,
+ boolean sslVerify)
+ throws IOException, GeneralSecurityException {
+ if (!(connection instanceof HttpClientConnection)) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ HttpApacheText.get().httpWrongConnectionType,
+ HttpClientConnection.class.getName(),
+ connection.getClass().getName()));
+ }
+ HttpClientConnection conn = (HttpClientConnection) connection;
+ String scheme = conn.getURL().getProtocol();
+ if (!"https".equals(scheme)) { //$NON-NLS-1$
+ return conn;
+ }
+ if (securityContext == null || isDefault != sslVerify) {
+ isDefault = sslVerify;
+ HostnameVerifier verifier;
+ if (sslVerify) {
+ securityContext = SSLContext.getDefault();
+ verifier = SSLConnectionSocketFactory
+ .getDefaultHostnameVerifier();
+ } else {
+ securityContext = SSLContext.getInstance("TLS");
+ TrustManager[] trustAllCerts = {
+ new NoCheckX509TrustManager() };
+ securityContext.init(null, trustAllCerts, null);
+ verifier = (name, session) -> true;
+ }
+ socketFactory = new SSLConnectionSocketFactory(securityContext,
+ verifier) {
+
+ @Override
+ protected void prepareSocket(SSLSocket socket)
+ throws IOException {
+ super.prepareSocket(socket);
+ HttpSupport.configureTLS(socket);
+ }
+ };
+ }
+ conn.setSSLSocketFactory(socketFactory, isDefault);
+ return conn;
+ }
+
+ @Override
+ public void close() {
+ securityContext = null;
+ socketFactory = null;
+ }
+
+ }
}
diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/internal/HttpApacheText.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/internal/HttpApacheText.java
index 907ab98cc8..677d7d792b 100644
--- a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/internal/HttpApacheText.java
+++ b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/internal/HttpApacheText.java
@@ -27,5 +27,6 @@ public class HttpApacheText extends TranslationBundle {
}
// @formatter:off
+ /***/ public String httpWrongConnectionType;
/***/ public String unexpectedSSLContextException;
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
index c3d72552a5..e90580b75f 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
@@ -20,7 +20,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
-import org.eclipse.jgit.internal.storage.file.PackFile;
+import org.eclipse.jgit.internal.storage.file.Pack;
import org.eclipse.jgit.lib.ObjectDatabase;
/** Sends the current list of pack files, sorted most recent first. */
@@ -38,7 +38,7 @@ class InfoPacksServlet extends HttpServlet {
final StringBuilder out = new StringBuilder();
final ObjectDatabase db = getRepository(req).getObjectDatabase();
if (db instanceof ObjectDirectory) {
- for (PackFile pack : ((ObjectDirectory) db).getPacks()) {
+ for (Pack pack : ((ObjectDirectory) db).getPacks()) {
out.append("P ");
out.append(pack.getPackFile().getName());
out.append('\n');
diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
index 3dd5840397..b853c6a7ed 100644
--- a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
@@ -51,8 +51,8 @@ org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index a63ebdaebf..c085e7eff9 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -12,7 +12,7 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
org.apache.commons.codec;version="[1.6.0,2.0.0)",
org.apache.commons.codec.binary;version="[1.6.0,2.0.0)",
org.apache.http;version="[4.3.0,5.0.0)",
- org.apache.http.client;version="[4.3.0,5.0.0)",
+ org.apache.http.client;version="[4.4.0,5.0.0)",
org.apache.http.message;version="[4.3.0,5.0.0)",
org.eclipse.jetty.continuation;version="[9.4.5,10.0.0)",
org.eclipse.jetty.http;version="[9.4.5,10.0.0)",
@@ -28,6 +28,7 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)",
+ org.eclipse.jgit.api;version="[6.0.0,6.1.0)",
org.eclipse.jgit.errors;version="[6.0.0,6.1.0)",
org.eclipse.jgit.http.server;version="[6.0.0,6.1.0)",
org.eclipse.jgit.http.server.glue;version="[6.0.0,6.1.0)",
diff --git a/org.eclipse.jgit.http.test/build.properties b/org.eclipse.jgit.http.test/build.properties
index e8bacac9ac..a909f1301f 100644
--- a/org.eclipse.jgit.http.test/build.properties
+++ b/org.eclipse.jgit.http.test/build.properties
@@ -4,3 +4,5 @@ output.. = bin/
bin.includes = META-INF/,\
.,\
plugin.properties
+additional.bundles = org.apache.log4j,\
+ org.slf4j.binding.log4j12
diff --git a/org.eclipse.jgit.http.test/src/org/eclipse/jgit/http/test/RefsUnreadableInMemoryRepository.java b/org.eclipse.jgit.http.test/src/org/eclipse/jgit/http/test/RefsUnreadableInMemoryRepository.java
index 80cbe8738c..4167b038e1 100644
--- a/org.eclipse.jgit.http.test/src/org/eclipse/jgit/http/test/RefsUnreadableInMemoryRepository.java
+++ b/org.eclipse.jgit.http.test/src/org/eclipse/jgit/http/test/RefsUnreadableInMemoryRepository.java
@@ -85,6 +85,17 @@ class RefsUnreadableInMemoryRepository extends InMemoryRepository {
/** {@inheritDoc} */
@Override
+ public List<Ref> getRefsByPrefixWithExclusions(String include, Set<String> excludes)
+ throws IOException {
+ if (failing) {
+ throw new IOException("disk failed, no refs found");
+ }
+
+ return super.getRefsByPrefixWithExclusions(include, excludes);
+ }
+
+ /** {@inheritDoc} */
+ @Override
public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
if (failing) {
throw new IOException("disk failed, no refs found");
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AllProtocolsHttpTestCase.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AllProtocolsHttpTestCase.java
new file mode 100644
index 0000000000..c6931ad5b9
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AllProtocolsHttpTestCase.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.http.test;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.jgit.junit.http.HttpTestCase;
+import org.eclipse.jgit.transport.HttpTransport;
+import org.eclipse.jgit.transport.http.HttpConnectionFactory;
+import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory;
+import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Abstract test base class for running HTTP-related tests with all connection
+ * factories provided in JGit and with both protocol V0 and V2.
+ */
+@Ignore
+@RunWith(Parameterized.class)
+public abstract class AllProtocolsHttpTestCase extends HttpTestCase {
+
+ protected static class TestParameters {
+
+ public final HttpConnectionFactory factory;
+
+ public final boolean enableProtocolV2;
+
+ public TestParameters(HttpConnectionFactory factory,
+ boolean enableProtocolV2) {
+ this.factory = factory;
+ this.enableProtocolV2 = enableProtocolV2;
+ }
+
+ @Override
+ public String toString() {
+ return factory.toString() + " protocol "
+ + (enableProtocolV2 ? "V2" : "V0");
+ }
+ }
+
+ @Parameters(name = "{0}")
+ public static Collection<TestParameters> data() {
+ // run all tests with both connection factories we have
+ HttpConnectionFactory[] factories = new HttpConnectionFactory[] {
+ new JDKHttpConnectionFactory() {
+
+ @Override
+ public String toString() {
+ return this.getClass().getSuperclass().getName();
+ }
+ }, new HttpClientConnectionFactory() {
+
+ @Override
+ public String toString() {
+ return this.getClass().getSuperclass().getName();
+ }
+ } };
+ List<TestParameters> result = new ArrayList<>();
+ for (HttpConnectionFactory factory : factories) {
+ result.add(new TestParameters(factory, false));
+ result.add(new TestParameters(factory, true));
+ }
+ return result;
+ }
+
+ protected final boolean enableProtocolV2;
+
+ protected AllProtocolsHttpTestCase(TestParameters params) {
+ HttpTransport.setConnectionFactory(params.factory);
+ enableProtocolV2 = params.enableProtocolV2;
+ }
+
+ private static HttpConnectionFactory originalFactory;
+
+ @BeforeClass
+ public static void saveConnectionFactory() {
+ originalFactory = HttpTransport.getConnectionFactory();
+ }
+
+ @AfterClass
+ public static void restoreConnectionFactory() {
+ HttpTransport.setConnectionFactory(originalFactory);
+ }
+
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
index 6da5f86b3e..8b28c4292c 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
@@ -35,6 +35,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.FetchConnection;
@@ -77,6 +78,9 @@ public class DumbClientDumbServerTest extends AllFactoriesHttpTestCase {
remoteRepository = src.getRepository();
remoteURI = toURIish(app, srcGit.getName());
+ StoredConfig cfg = remoteRepository.getConfig();
+ cfg.setInt("protocol", null, "version", 0);
+ cfg.save();
A_txt = src.blob("A");
A = src.commit().add("A_txt", A_txt).create();
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
index ccde1fe55c..986b5ca92b 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
@@ -35,6 +35,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.FetchConnection;
@@ -42,11 +43,10 @@ import org.eclipse.jgit.transport.HttpTransport;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.TransportHttp;
import org.eclipse.jgit.transport.URIish;
-import org.eclipse.jgit.transport.http.HttpConnectionFactory;
import org.junit.Before;
import org.junit.Test;
-public class DumbClientSmartServerTest extends AllFactoriesHttpTestCase {
+public class DumbClientSmartServerTest extends AllProtocolsHttpTestCase {
private Repository remoteRepository;
private URIish remoteURI;
@@ -55,8 +55,8 @@ public class DumbClientSmartServerTest extends AllFactoriesHttpTestCase {
private RevCommit A, B;
- public DumbClientSmartServerTest(HttpConnectionFactory cf) {
- super(cf);
+ public DumbClientSmartServerTest(TestParameters params) {
+ super(params);
}
@Override
@@ -76,6 +76,9 @@ public class DumbClientSmartServerTest extends AllFactoriesHttpTestCase {
remoteRepository = src.getRepository();
remoteURI = toURIish(app, srcName);
+ StoredConfig cfg = remoteRepository.getConfig();
+ cfg.setInt("protocol", null, "version", enableProtocolV2 ? 2 : 0);
+ cfg.save();
A_txt = src.blob("A");
A = src.commit().add("A_txt", A_txt).create();
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
index 96657761cf..df093c185b 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
@@ -324,7 +324,22 @@ public class HttpClientTests extends AllFactoriesHttpTestCase {
}
@Test
- public void testHttpClientWantsV2ButServerNotConfigured() throws Exception {
+ public void testHttpClientWantsV2AndServerNotConfigured() throws Exception {
+ String url = smartAuthNoneURI.toString() + "/info/refs?service=git-upload-pack";
+ HttpConnection c = HttpTransport.getConnectionFactory()
+ .create(new URL(url));
+ c.setRequestMethod("GET");
+ c.setRequestProperty("Git-Protocol", "version=2");
+ assertEquals(200, c.getResponseCode());
+
+ PacketLineIn pckIn = new PacketLineIn(c.getInputStream());
+ assertThat(pckIn.readString(), is("version 2"));
+ }
+
+ @Test
+ public void testHttpServerConfiguredToV0() throws Exception {
+ remoteRepository.getRepository().getConfig().setInt(
+ "protocol", null, "version", 0);
String url = smartAuthNoneURI.toString() + "/info/refs?service=git-upload-pack";
HttpConnection c = HttpTransport.getConnectionFactory()
.create(new URL(url));
@@ -342,9 +357,6 @@ public class HttpClientTests extends AllFactoriesHttpTestCase {
@Test
public void testV2HttpFirstResponse() throws Exception {
- remoteRepository.getRepository().getConfig().setInt(
- "protocol", null, "version", 2);
-
String url = smartAuthNoneURI.toString() + "/info/refs?service=git-upload-pack";
HttpConnection c = HttpTransport.getConnectionFactory()
.create(new URL(url));
@@ -364,9 +376,6 @@ public class HttpClientTests extends AllFactoriesHttpTestCase {
@Test
public void testV2HttpSubsequentResponse() throws Exception {
- remoteRepository.getRepository().getConfig().setInt(
- "protocol", null, "version", 2);
-
String url = smartAuthNoneURI.toString() + "/git-upload-pack";
HttpConnection c = HttpTransport.getConnectionFactory()
.create(new URL(url));
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerSslTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerSslTest.java
index 597fb2e507..7bc50cad89 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerSslTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerSslTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2017, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -41,6 +41,7 @@ import org.eclipse.jgit.junit.http.AppServer;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.CredentialItem;
@@ -48,7 +49,6 @@ import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
-import org.eclipse.jgit.transport.http.HttpConnectionFactory;
import org.eclipse.jgit.util.HttpSupport;
import org.junit.Before;
import org.junit.Test;
@@ -56,7 +56,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class SmartClientSmartServerSslTest extends AllFactoriesHttpTestCase {
+public class SmartClientSmartServerSslTest extends AllProtocolsHttpTestCase {
// We run these tests with a server on localhost with a self-signed
// certificate. We don't do authentication tests here, so there's no need
@@ -112,8 +112,8 @@ public class SmartClientSmartServerSslTest extends AllFactoriesHttpTestCase {
private RevCommit A, B;
- public SmartClientSmartServerSslTest(HttpConnectionFactory cf) {
- super(cf);
+ public SmartClientSmartServerSslTest(TestParameters params) {
+ super(params);
}
@Override
@@ -128,10 +128,11 @@ public class SmartClientSmartServerSslTest extends AllFactoriesHttpTestCase {
final TestRepository<Repository> src = createTestRepository();
final String srcName = src.getRepository().getDirectory().getName();
- src.getRepository()
- .getConfig()
- .setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
+ StoredConfig cfg = src.getRepository().getConfig();
+ cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
+ cfg.setInt("protocol", null, "version", enableProtocolV2 ? 2 : 0);
+ cfg.save();
GitServlet gs = new GitServlet();
@@ -238,7 +239,7 @@ public class SmartClientSmartServerSslTest extends AllFactoriesHttpTestCase {
fsck(dst, B);
List<AccessEvent> requests = getRequests();
- assertEquals(2, requests.size());
+ assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
}
@Test
@@ -256,7 +257,7 @@ public class SmartClientSmartServerSslTest extends AllFactoriesHttpTestCase {
fsck(dst, B);
List<AccessEvent> requests = getRequests();
- assertEquals(3, requests.size());
+ assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
}
@Test
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
index 8d1870a87e..887e970a0c 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, 2017 Google Inc. and others
+ * Copyright (C) 2010, 2020 Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -22,10 +22,17 @@ import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.IOException;
+import java.io.OutputStreamWriter;
import java.io.PrintWriter;
+import java.io.Writer;
+import java.net.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.EnumSet;
@@ -48,6 +55,8 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.TransportConfigCallback;
import org.eclipse.jgit.errors.RemoteRepositoryException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
@@ -70,6 +79,7 @@ import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.lib.TextProgressMonitor;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -86,13 +96,14 @@ import org.eclipse.jgit.transport.TransportHttp;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.transport.UploadPack;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
+import org.eclipse.jgit.transport.http.HttpConnection;
import org.eclipse.jgit.transport.http.HttpConnectionFactory;
import org.eclipse.jgit.util.HttpSupport;
import org.eclipse.jgit.util.SystemReader;
import org.junit.Before;
import org.junit.Test;
-public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
+public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase {
private static final String HDR_TRANSFER_ENCODING = "Transfer-Encoding";
private AdvertiseRefsHook advertiseRefsHook;
@@ -120,8 +131,8 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
private RevCommit A, B, unreachableCommit;
- public SmartClientSmartServerTest(HttpConnectionFactory cf) {
- super(cf);
+ public SmartClientSmartServerTest(TestParameters params) {
+ super(params);
}
@Override
@@ -131,10 +142,11 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
final TestRepository<Repository> src = createTestRepository();
final String srcName = src.getRepository().getDirectory().getName();
- src.getRepository()
- .getConfig()
- .setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
+ StoredConfig cfg = src.getRepository().getConfig();
+ cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
+ cfg.setInt("protocol", null, "version", enableProtocolV2 ? 2 : 0);
+ cfg.save();
GitServlet gs = new GitServlet();
gs.setUploadPackFactory((HttpServletRequest req, Repository db) -> {
@@ -448,7 +460,7 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
assertEquals(B, map.get(Constants.HEAD).getObjectId());
List<AccessEvent> requests = getRequests();
- assertEquals(1, requests.size());
+ assertEquals(enableProtocolV2 ? 2 : 1, requests.size());
AccessEvent info = requests.get(0);
assertEquals("GET", info.getMethod());
@@ -458,7 +470,22 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
assertEquals(200, info.getStatus());
assertEquals("application/x-git-upload-pack-advertisement", info
.getResponseHeader(HDR_CONTENT_TYPE));
- assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ if (!enableProtocolV2) {
+ assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ } else {
+ AccessEvent lsRefs = requests.get(1);
+ assertEquals("POST", lsRefs.getMethod());
+ assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
+ assertEquals(0, lsRefs.getParameters().size());
+ assertNotNull("has content-length",
+ lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
+ assertNull("not chunked",
+ lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
+ assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
+ assertEquals(200, lsRefs.getStatus());
+ assertEquals("application/x-git-upload-pack-result",
+ lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
+ }
}
@Test
@@ -576,9 +603,10 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
}
List<AccessEvent> requests = getRequests();
- assertEquals(2, requests.size());
+ assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
- AccessEvent info = requests.get(0);
+ int requestNumber = 0;
+ AccessEvent info = requests.get(requestNumber++);
assertEquals("GET", info.getMethod());
assertEquals(join(remoteURI, "info/refs"), info.getPath());
assertEquals(1, info.getParameters().size());
@@ -586,9 +614,24 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
assertEquals(200, info.getStatus());
assertEquals("application/x-git-upload-pack-advertisement", info
.getResponseHeader(HDR_CONTENT_TYPE));
- assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ if (!enableProtocolV2) {
+ assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ } else {
+ AccessEvent lsRefs = requests.get(requestNumber++);
+ assertEquals("POST", lsRefs.getMethod());
+ assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
+ assertEquals(0, lsRefs.getParameters().size());
+ assertNotNull("has content-length",
+ lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
+ assertNull("not chunked",
+ lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
+ assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
+ assertEquals(200, lsRefs.getStatus());
+ assertEquals("application/x-git-upload-pack-result",
+ lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
+ }
- AccessEvent service = requests.get(1);
+ AccessEvent service = requests.get(requestNumber);
assertEquals("POST", service.getMethod());
assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
assertEquals(0, service.getParameters().size());
@@ -602,6 +645,63 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
.getResponseHeader(HDR_CONTENT_TYPE));
}
+ @Test
+ public void test_CloneWithCustomFactory() throws Exception {
+ HttpConnectionFactory globalFactory = HttpTransport
+ .getConnectionFactory();
+ HttpConnectionFactory failingConnectionFactory = new HttpConnectionFactory() {
+
+ @Override
+ public HttpConnection create(URL url) throws IOException {
+ throw new IOException("Should not be reached");
+ }
+
+ @Override
+ public HttpConnection create(URL url, Proxy proxy)
+ throws IOException {
+ throw new IOException("Should not be reached");
+ }
+ };
+ HttpTransport.setConnectionFactory(failingConnectionFactory);
+ try {
+ File tmp = createTempDirectory("cloneViaApi");
+ boolean[] localFactoryUsed = { false };
+ TransportConfigCallback callback = new TransportConfigCallback() {
+
+ @Override
+ public void configure(Transport transport) {
+ if (transport instanceof TransportHttp) {
+ ((TransportHttp) transport).setHttpConnectionFactory(
+ new HttpConnectionFactory() {
+
+ @Override
+ public HttpConnection create(URL url)
+ throws IOException {
+ localFactoryUsed[0] = true;
+ return globalFactory.create(url);
+ }
+
+ @Override
+ public HttpConnection create(URL url,
+ Proxy proxy) throws IOException {
+ localFactoryUsed[0] = true;
+ return globalFactory.create(url, proxy);
+ }
+ });
+ }
+ }
+ };
+ try (Git git = Git.cloneRepository().setDirectory(tmp)
+ .setTransportConfigCallback(callback)
+ .setURI(remoteURI.toPrivateString()).call()) {
+ assertTrue("Should have used the local HttpConnectionFactory",
+ localFactoryUsed[0]);
+ }
+ } finally {
+ HttpTransport.setConnectionFactory(globalFactory);
+ }
+ }
+
private void initialClone_Redirect(int nofRedirects, int code)
throws Exception {
initialClone_Redirect(nofRedirects, code, false);
@@ -628,7 +728,8 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
}
List<AccessEvent> requests = getRequests();
- assertEquals(2 + nofRedirects, requests.size());
+ assertEquals((enableProtocolV2 ? 3 : 2) + nofRedirects,
+ requests.size());
int n = 0;
while (n < nofRedirects) {
@@ -644,7 +745,22 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
assertEquals(200, info.getStatus());
assertEquals("application/x-git-upload-pack-advertisement",
info.getResponseHeader(HDR_CONTENT_TYPE));
- assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ if (!enableProtocolV2) {
+ assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ } else {
+ AccessEvent lsRefs = requests.get(n++);
+ assertEquals("POST", lsRefs.getMethod());
+ assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
+ assertEquals(0, lsRefs.getParameters().size());
+ assertNotNull("has content-length",
+ lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
+ assertNull("not chunked",
+ lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
+ assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
+ assertEquals(200, lsRefs.getStatus());
+ assertEquals("application/x-git-upload-pack-result",
+ lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
+ }
AccessEvent service = requests.get(n++);
assertEquals("POST", service.getMethod());
@@ -756,7 +872,7 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
}
List<AccessEvent> requests = getRequests();
- assertEquals(3, requests.size());
+ assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
AccessEvent info = requests.get(0);
assertEquals("GET", info.getMethod());
@@ -766,24 +882,27 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
assertEquals(200, info.getStatus());
assertEquals("application/x-git-upload-pack-advertisement",
info.getResponseHeader(HDR_CONTENT_TYPE));
- assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ if (!enableProtocolV2) {
+ assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ }
AccessEvent redirect = requests.get(1);
assertEquals("POST", redirect.getMethod());
assertEquals(301, redirect.getStatus());
- AccessEvent service = requests.get(2);
- assertEquals("POST", service.getMethod());
- assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
- assertEquals(0, service.getParameters().size());
- assertNotNull("has content-length",
- service.getRequestHeader(HDR_CONTENT_LENGTH));
- assertNull("not chunked",
- service.getRequestHeader(HDR_TRANSFER_ENCODING));
-
- assertEquals(200, service.getStatus());
- assertEquals("application/x-git-upload-pack-result",
- service.getResponseHeader(HDR_CONTENT_TYPE));
+ for (int i = 2; i < requests.size(); i++) {
+ AccessEvent service = requests.get(i);
+ assertEquals("POST", service.getMethod());
+ assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
+ assertEquals(0, service.getParameters().size());
+ assertNotNull("has content-length",
+ service.getRequestHeader(HDR_CONTENT_LENGTH));
+ assertNull("not chunked",
+ service.getRequestHeader(HDR_TRANSFER_ENCODING));
+ assertEquals(200, service.getStatus());
+ assertEquals("application/x-git-upload-pack-result",
+ service.getResponseHeader(HDR_CONTENT_TYPE));
+ }
}
@Test
@@ -817,6 +936,35 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
}
}
+ private void assertFetchRequests(List<AccessEvent> requests, int index) {
+ AccessEvent info = requests.get(index++);
+ assertEquals("GET", info.getMethod());
+ assertEquals(join(authURI, "info/refs"), info.getPath());
+ assertEquals(1, info.getParameters().size());
+ assertEquals("git-upload-pack", info.getParameter("service"));
+ assertEquals(200, info.getStatus());
+ assertEquals("application/x-git-upload-pack-advertisement",
+ info.getResponseHeader(HDR_CONTENT_TYPE));
+ if (!enableProtocolV2) {
+ assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ }
+
+ for (int i = index; i < requests.size(); i++) {
+ AccessEvent service = requests.get(i);
+ assertEquals("POST", service.getMethod());
+ assertEquals(join(authURI, "git-upload-pack"), service.getPath());
+ assertEquals(0, service.getParameters().size());
+ assertNotNull("has content-length",
+ service.getRequestHeader(HDR_CONTENT_LENGTH));
+ assertNull("not chunked",
+ service.getRequestHeader(HDR_TRANSFER_ENCODING));
+
+ assertEquals(200, service.getStatus());
+ assertEquals("application/x-git-upload-pack-result",
+ service.getResponseHeader(HDR_CONTENT_TYPE));
+ }
+ }
+
@Test
public void testInitialClone_WithAuthentication() throws Exception {
try (Repository dst = createBareRepository();
@@ -830,34 +978,167 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
}
List<AccessEvent> requests = getRequests();
- assertEquals(3, requests.size());
+ assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
AccessEvent info = requests.get(0);
assertEquals("GET", info.getMethod());
assertEquals(401, info.getStatus());
- info = requests.get(1);
+ assertFetchRequests(requests, 1);
+ }
+
+ @Test
+ public void testInitialClone_WithPreAuthentication() throws Exception {
+ try (Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, authURI)) {
+ assertFalse(dst.getObjectDatabase().has(A_txt));
+ ((TransportHttp) t).setPreemptiveBasicAuthentication(
+ AppServer.username, AppServer.password);
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ assertTrue(dst.getObjectDatabase().has(A_txt));
+ assertEquals(B, dst.exactRef(master).getObjectId());
+ fsck(dst, B);
+ }
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
+
+ assertFetchRequests(requests, 0);
+ }
+
+ @Test
+ public void testInitialClone_WithPreAuthenticationCleared()
+ throws Exception {
+ try (Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, authURI)) {
+ assertFalse(dst.getObjectDatabase().has(A_txt));
+ ((TransportHttp) t).setPreemptiveBasicAuthentication(
+ AppServer.username, AppServer.password);
+ ((TransportHttp) t).setPreemptiveBasicAuthentication(null, null);
+ t.setCredentialsProvider(testCredentials);
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ assertTrue(dst.getObjectDatabase().has(A_txt));
+ assertEquals(B, dst.exactRef(master).getObjectId());
+ fsck(dst, B);
+ }
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
+
+ AccessEvent info = requests.get(0);
assertEquals("GET", info.getMethod());
- assertEquals(join(authURI, "info/refs"), info.getPath());
- assertEquals(1, info.getParameters().size());
- assertEquals("git-upload-pack", info.getParameter("service"));
- assertEquals(200, info.getStatus());
- assertEquals("application/x-git-upload-pack-advertisement",
- info.getResponseHeader(HDR_CONTENT_TYPE));
- assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ assertEquals(401, info.getStatus());
- AccessEvent service = requests.get(2);
- assertEquals("POST", service.getMethod());
- assertEquals(join(authURI, "git-upload-pack"), service.getPath());
- assertEquals(0, service.getParameters().size());
- assertNotNull("has content-length",
- service.getRequestHeader(HDR_CONTENT_LENGTH));
- assertNull("not chunked",
- service.getRequestHeader(HDR_TRANSFER_ENCODING));
+ assertFetchRequests(requests, 1);
+ }
- assertEquals(200, service.getStatus());
- assertEquals("application/x-git-upload-pack-result",
- service.getResponseHeader(HDR_CONTENT_TYPE));
+ @Test
+ public void testInitialClone_PreAuthenticationTooLate() throws Exception {
+ try (Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, authURI)) {
+ assertFalse(dst.getObjectDatabase().has(A_txt));
+ ((TransportHttp) t).setPreemptiveBasicAuthentication(
+ AppServer.username, AppServer.password);
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ assertTrue(dst.getObjectDatabase().has(A_txt));
+ assertEquals(B, dst.exactRef(master).getObjectId());
+ fsck(dst, B);
+ List<AccessEvent> requests = getRequests();
+ assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
+ assertFetchRequests(requests, 0);
+ assertThrows(IllegalStateException.class,
+ () -> ((TransportHttp) t).setPreemptiveBasicAuthentication(
+ AppServer.username, AppServer.password));
+ assertThrows(IllegalStateException.class, () -> ((TransportHttp) t)
+ .setPreemptiveBasicAuthentication(null, null));
+ }
+ }
+
+ @Test
+ public void testInitialClone_WithWrongPreAuthenticationAndCredentialProvider()
+ throws Exception {
+ try (Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, authURI)) {
+ assertFalse(dst.getObjectDatabase().has(A_txt));
+ ((TransportHttp) t).setPreemptiveBasicAuthentication(
+ AppServer.username, AppServer.password + 'x');
+ t.setCredentialsProvider(testCredentials);
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ assertTrue(dst.getObjectDatabase().has(A_txt));
+ assertEquals(B, dst.exactRef(master).getObjectId());
+ fsck(dst, B);
+ }
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
+
+ AccessEvent info = requests.get(0);
+ assertEquals("GET", info.getMethod());
+ assertEquals(401, info.getStatus());
+
+ assertFetchRequests(requests, 1);
+ }
+
+ @Test
+ public void testInitialClone_WithWrongPreAuthentication() throws Exception {
+ try (Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, authURI)) {
+ assertFalse(dst.getObjectDatabase().has(A_txt));
+ ((TransportHttp) t).setPreemptiveBasicAuthentication(
+ AppServer.username, AppServer.password + 'x');
+ TransportException e = assertThrows(TransportException.class,
+ () -> t.fetch(NullProgressMonitor.INSTANCE,
+ mirror(master)));
+ String msg = e.getMessage();
+ assertTrue("Unexpected exception message: " + msg,
+ msg.contains("no CredentialsProvider"));
+ }
+ List<AccessEvent> requests = getRequests();
+ assertEquals(1, requests.size());
+
+ AccessEvent info = requests.get(0);
+ assertEquals("GET", info.getMethod());
+ assertEquals(401, info.getStatus());
+ }
+
+ @Test
+ public void testInitialClone_WithUserInfo() throws Exception {
+ URIish withUserInfo = authURI.setUser(AppServer.username)
+ .setPass(AppServer.password);
+ try (Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, withUserInfo)) {
+ assertFalse(dst.getObjectDatabase().has(A_txt));
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ assertTrue(dst.getObjectDatabase().has(A_txt));
+ assertEquals(B, dst.exactRef(master).getObjectId());
+ fsck(dst, B);
+ }
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
+
+ assertFetchRequests(requests, 0);
+ }
+
+ @Test
+ public void testInitialClone_PreAuthOverridesUserInfo() throws Exception {
+ URIish withUserInfo = authURI.setUser(AppServer.username)
+ .setPass(AppServer.password + 'x');
+ try (Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, withUserInfo)) {
+ assertFalse(dst.getObjectDatabase().has(A_txt));
+ ((TransportHttp) t).setPreemptiveBasicAuthentication(
+ AppServer.username, AppServer.password);
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ assertTrue(dst.getObjectDatabase().has(A_txt));
+ assertEquals(B, dst.exactRef(master).getObjectId());
+ fsck(dst, B);
+ }
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
+
+ assertFetchRequests(requests, 0);
}
@Test
@@ -937,19 +1218,20 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
}
List<AccessEvent> requests = getRequests();
- assertEquals(4, requests.size());
+ assertEquals(enableProtocolV2 ? 5 : 4, requests.size());
- AccessEvent redirect = requests.get(0);
+ int requestNumber = 0;
+ AccessEvent redirect = requests.get(requestNumber++);
assertEquals("GET", redirect.getMethod());
assertEquals(join(cloneFrom, "info/refs"), redirect.getPath());
assertEquals(301, redirect.getStatus());
- AccessEvent info = requests.get(1);
+ AccessEvent info = requests.get(requestNumber++);
assertEquals("GET", info.getMethod());
assertEquals(join(authURI, "info/refs"), info.getPath());
assertEquals(401, info.getStatus());
- info = requests.get(2);
+ info = requests.get(requestNumber++);
assertEquals("GET", info.getMethod());
assertEquals(join(authURI, "info/refs"), info.getPath());
assertEquals(1, info.getParameters().size());
@@ -957,9 +1239,24 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
assertEquals(200, info.getStatus());
assertEquals("application/x-git-upload-pack-advertisement",
info.getResponseHeader(HDR_CONTENT_TYPE));
- assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ if (!enableProtocolV2) {
+ assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ } else {
+ AccessEvent lsRefs = requests.get(requestNumber++);
+ assertEquals("POST", lsRefs.getMethod());
+ assertEquals(join(authURI, "git-upload-pack"), lsRefs.getPath());
+ assertEquals(0, lsRefs.getParameters().size());
+ assertNotNull("has content-length",
+ lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
+ assertNull("not chunked",
+ lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
+ assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
+ assertEquals(200, lsRefs.getStatus());
+ assertEquals("application/x-git-upload-pack-result",
+ lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
+ }
- AccessEvent service = requests.get(3);
+ AccessEvent service = requests.get(requestNumber);
assertEquals("POST", service.getMethod());
assertEquals(join(authURI, "git-upload-pack"), service.getPath());
assertEquals(0, service.getParameters().size());
@@ -987,7 +1284,7 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
}
List<AccessEvent> requests = getRequests();
- assertEquals(3, requests.size());
+ assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
AccessEvent info = requests.get(0);
assertEquals("GET", info.getMethod());
@@ -997,25 +1294,30 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
assertEquals(200, info.getStatus());
assertEquals("application/x-git-upload-pack-advertisement",
info.getResponseHeader(HDR_CONTENT_TYPE));
- assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ if (!enableProtocolV2) {
+ assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+ }
AccessEvent service = requests.get(1);
assertEquals("POST", service.getMethod());
assertEquals(join(authOnPostURI, "git-upload-pack"), service.getPath());
assertEquals(401, service.getStatus());
- service = requests.get(2);
- assertEquals("POST", service.getMethod());
- assertEquals(join(authOnPostURI, "git-upload-pack"), service.getPath());
- assertEquals(0, service.getParameters().size());
- assertNotNull("has content-length",
- service.getRequestHeader(HDR_CONTENT_LENGTH));
- assertNull("not chunked",
- service.getRequestHeader(HDR_TRANSFER_ENCODING));
-
- assertEquals(200, service.getStatus());
- assertEquals("application/x-git-upload-pack-result",
- service.getResponseHeader(HDR_CONTENT_TYPE));
+ for (int i = 2; i < requests.size(); i++) {
+ service = requests.get(i);
+ assertEquals("POST", service.getMethod());
+ assertEquals(join(authOnPostURI, "git-upload-pack"),
+ service.getPath());
+ assertEquals(0, service.getParameters().size());
+ assertNotNull("has content-length",
+ service.getRequestHeader(HDR_CONTENT_LENGTH));
+ assertNull("not chunked",
+ service.getRequestHeader(HDR_TRANSFER_ENCODING));
+
+ assertEquals(200, service.getStatus());
+ assertEquals("application/x-git-upload-pack-result",
+ service.getResponseHeader(HDR_CONTENT_TYPE));
+ }
}
@Test
@@ -1052,9 +1354,11 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
List<AccessEvent> requests = getRequests();
requests.removeAll(cloneRequests);
- assertEquals(2, requests.size());
- AccessEvent info = requests.get(0);
+ assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
+
+ int requestNumber = 0;
+ AccessEvent info = requests.get(requestNumber++);
assertEquals("GET", info.getMethod());
assertEquals(join(remoteURI, "info/refs"), info.getPath());
assertEquals(1, info.getParameters().size());
@@ -1063,9 +1367,24 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
assertEquals("application/x-git-upload-pack-advertisement",
info.getResponseHeader(HDR_CONTENT_TYPE));
+ if (enableProtocolV2) {
+ AccessEvent lsRefs = requests.get(requestNumber++);
+ assertEquals("POST", lsRefs.getMethod());
+ assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
+ assertEquals(0, lsRefs.getParameters().size());
+ assertNotNull("has content-length",
+ lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
+ assertNull("not chunked",
+ lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
+ assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
+ assertEquals(200, lsRefs.getStatus());
+ assertEquals("application/x-git-upload-pack-result",
+ lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
+ }
+
// We should have needed one request to perform the fetch.
//
- AccessEvent service = requests.get(1);
+ AccessEvent service = requests.get(requestNumber);
assertEquals("POST", service.getMethod());
assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
assertEquals(0, service.getParameters().size());
@@ -1116,9 +1435,10 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
List<AccessEvent> requests = getRequests();
requests.removeAll(cloneRequests);
- assertEquals(3, requests.size());
+ assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
- AccessEvent info = requests.get(0);
+ int requestNumber = 0;
+ AccessEvent info = requests.get(requestNumber++);
assertEquals("GET", info.getMethod());
assertEquals(join(remoteURI, "info/refs"), info.getPath());
assertEquals(1, info.getParameters().size());
@@ -1127,10 +1447,25 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
assertEquals("application/x-git-upload-pack-advertisement", info
.getResponseHeader(HDR_CONTENT_TYPE));
+ if (enableProtocolV2) {
+ AccessEvent lsRefs = requests.get(requestNumber++);
+ assertEquals("POST", lsRefs.getMethod());
+ assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
+ assertEquals(0, lsRefs.getParameters().size());
+ assertNotNull("has content-length",
+ lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
+ assertNull("not chunked",
+ lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
+ assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
+ assertEquals(200, lsRefs.getStatus());
+ assertEquals("application/x-git-upload-pack-result",
+ lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
+ }
+
// We should have needed two requests to perform the fetch
// due to the high number of local unknown commits.
//
- AccessEvent service = requests.get(1);
+ AccessEvent service = requests.get(requestNumber++);
assertEquals("POST", service.getMethod());
assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
assertEquals(0, service.getParameters().size());
@@ -1143,7 +1478,7 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
assertEquals("application/x-git-upload-pack-result", service
.getResponseHeader(HDR_CONTENT_TYPE));
- service = requests.get(2);
+ service = requests.get(requestNumber);
assertEquals("POST", service.getMethod());
assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
assertEquals(0, service.getParameters().size());
@@ -1158,6 +1493,64 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
}
@Test
+ public void testFetch_MaxHavesCutoffAfterAckOnly() throws Exception {
+ // Bootstrap by doing the clone.
+ //
+ TestRepository dst = createTestRepository();
+ try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ }
+ assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
+
+ // Force enough into the local client that enumeration will
+ // need more than MAX_HAVES (256) haves to be sent. The server
+ // doesn't know any of these, so it will never ACK. The client
+ // should keep going.
+ //
+ // If it does, client and server will find a common commit, and
+ // the pack file will contain exactly the one commit object Z
+ // we create on the remote, which we can test for via the progress
+ // monitor, which should have something like
+ // "Receiving objects: 100% (1/1)". If the client sends a "done"
+ // too early, the server will send more objects, and we'll have
+ // a line like "Receiving objects: 100% (8/8)".
+ TestRepository.BranchBuilder b = dst.branch(master);
+ // The client will send 32 + 64 + 128 + 256 + 512 haves. Only the
+ // last one will be a common commit. If the cutoff kicks in too
+ // early (after 480), we'll get too many objects in the fetch.
+ for (int i = 0; i < 992; i++)
+ b.commit().tick(3600 /* 1 hour */).message("c" + i).create();
+
+ // Create a new commit on the remote.
+ //
+ RevCommit Z;
+ try (TestRepository<Repository> tr = new TestRepository<>(
+ remoteRepository)) {
+ b = tr.branch(master);
+ Z = b.commit().message("Z").create();
+ }
+
+ // Now incrementally update.
+ //
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ Writer writer = new OutputStreamWriter(buffer, StandardCharsets.UTF_8);
+ TextProgressMonitor monitor = new TextProgressMonitor(writer);
+ try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
+ t.fetch(monitor, mirror(master));
+ }
+ assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
+
+ String progressMessages = new String(buffer.toByteArray(),
+ StandardCharsets.UTF_8);
+ Pattern expected = Pattern
+ .compile("Receiving objects:\\s+100% \\(1/1\\)\n");
+ if (!expected.matcher(progressMessages).find()) {
+ System.out.println(progressMessages);
+ fail("Expected only one object to be sent");
+ }
+ }
+
+ @Test
public void testInitialClone_BrokenServer() throws Exception {
try (Repository dst = createBareRepository();
Transport t = Transport.open(dst, brokenURI)) {
@@ -1211,7 +1604,8 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
Collections.<ObjectId> emptySet());
fail("Server accepted want " + id.name());
} catch (TransportException err) {
- assertEquals("want " + id.name() + " not valid", err.getMessage());
+ assertTrue(err.getMessage()
+ .contains("want " + id.name() + " not valid"));
}
}
@@ -1224,6 +1618,8 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
new DfsRepositoryDescription(repoName));
final TestRepository<Repository> repo = new TestRepository<>(
badRefsRepo);
+ badRefsRepo.getConfig().setInt("protocol", null, "version",
+ enableProtocolV2 ? 2 : 0);
ServletContextHandler app = noRefServer.addContext("/git");
GitServlet gs = new GitServlet();
@@ -1253,7 +1649,8 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
Collections.<ObjectId> emptySet());
fail("Successfully served ref with value " + c.getRef(master));
} catch (TransportException err) {
- assertEquals("Internal server error", err.getMessage());
+ assertTrue("Unexpected exception message " + err.getMessage(),
+ err.getMessage().contains("Internal server error"));
}
} finally {
noRefServer.tearDown();
@@ -1429,5 +1826,4 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
cfg.setBoolean("http", null, "receivepack", true);
cfg.save();
}
-
}
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
index 4e27a3d351..f9f8c856b8 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
@@ -23,8 +23,8 @@ import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.security.AbstractLoginService;
@@ -259,7 +259,7 @@ public class AppServer {
static class TestMappedLoginService extends AbstractLoginService {
private String role;
- protected final ConcurrentMap<String, UserPrincipal> users = new ConcurrentHashMap<>();
+ protected final Map<String, UserPrincipal> users = new ConcurrentHashMap<>();
TestMappedLoginService(String role) {
this.role = role;
diff --git a/org.eclipse.jgit.junit.ssh/.settings/edu.umd.cs.findbugs.core.prefs b/org.eclipse.jgit.junit.ssh/.settings/edu.umd.cs.findbugs.core.prefs
new file mode 100644
index 0000000000..2792ea032a
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.settings/edu.umd.cs.findbugs.core.prefs
@@ -0,0 +1,145 @@
+#SpotBugs User Preferences
+#Fri Dec 04 11:26:04 CET 2020
+detectorExplicitSerialization=ExplicitSerialization|true
+detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
+detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
+detectorWrongMapIterator=WrongMapIterator|true
+detectorUnnecessaryMath=UnnecessaryMath|true
+detectorUselessSubclassMethod=UselessSubclassMethod|false
+filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,EXPERIMENTAL,I18N,MALICIOUS_CODE,MT_CORRECTNESS,PERFORMANCE,SECURITY,STYLE|false|15
+detectorURLProblems=URLProblems|true
+detectorIteratorIdioms=IteratorIdioms|true
+detectorMutableEnum=MutableEnum|true
+detectorFindNonShortCircuit=FindNonShortCircuit|true
+detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
+detectorVolatileUsage=VolatileUsage|true
+detectorFindNakedNotify=FindNakedNotify|true
+detectorFindUninitializedGet=FindUninitializedGet|true
+detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true
+detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
+detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
+detectorSwitchFallthrough=SwitchFallthrough|true
+detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
+detectorConfusedInheritance=ConfusedInheritance|true
+detectorSynchronizationOnSharedBuiltinConstant=SynchronizationOnSharedBuiltinConstant|true
+detectorMutableStaticFields=MutableStaticFields|true
+detectorInvalidJUnitTest=InvalidJUnitTest|true
+detectorInfiniteLoop=InfiniteLoop|true
+detectorFindRunInvocations=FindRunInvocations|true
+detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
+detectorXMLFactoryBypass=XMLFactoryBypass|true
+detectorFindOpenStream=FindOpenStream|true
+detectorCheckExpectedWarnings=CheckExpectedWarnings|false
+detectorHugeSharedStringConstants=HugeSharedStringConstants|true
+detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true
+detectorStringConcatenation=StringConcatenation|true
+detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
+detectorFinalizerNullsFields=FinalizerNullsFields|true
+detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
+detectorInefficientToArray=InefficientToArray|false
+detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
+detectorInconsistentAnnotations=InconsistentAnnotations|true
+detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
+detectorInstantiateStaticClass=InstantiateStaticClass|true
+detectorCheckRelaxingNullnessAnnotation=CheckRelaxingNullnessAnnotation|true
+detectorMethodReturnCheck=MethodReturnCheck|true
+detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
+detectorFindDoubleCheck=FindDoubleCheck|true
+detectorFindBadForLoop=FindBadForLoop|true
+detectorDefaultEncodingDetector=DefaultEncodingDetector|true
+detectorFindInconsistentSync2=FindInconsistentSync2|true
+detectorFindSpinLoop=FindSpinLoop|true
+detectorFindMaskedFields=FindMaskedFields|true
+detectorBooleanReturnNull=BooleanReturnNull|true
+detectorFindUnsyncGet=FindUnsyncGet|true
+detectorCrossSiteScripting=CrossSiteScripting|true
+detectorDroppedException=DroppedException|true
+detectorFindDeadLocalStores=FindDeadLocalStores|true
+detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
+detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
+detectorFindRefComparison=FindRefComparison|true
+detectorFindRoughConstants=FindRoughConstants|true
+detectorMutableLock=MutableLock|true
+detectorFindNullDeref=FindNullDeref|true
+detectorFindReturnRef=FindReturnRef|true
+detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
+detectorFindUselessControlFlow=FindUselessControlFlow|true
+detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
+detectorIDivResultCastToDouble=IDivResultCastToDouble|true
+detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true
+detectorFindSelfComparison=FindSelfComparison|true
+detectorFindFloatEquality=FindFloatEquality|true
+detectorFindComparatorProblems=FindComparatorProblems|true
+detectorRepeatedConditionals=RepeatedConditionals|true
+filter_settings_neg=NOISE|
+detectorInefficientMemberAccess=InefficientMemberAccess|false
+detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
+detectorNumberConstructor=NumberConstructor|true
+detectorDontAssertInstanceofInTests=DontAssertInstanceofInTests|true
+detectorFindFinalizeInvocations=FindFinalizeInvocations|true
+detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
+detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true
+detectorFindUnconditionalWait=FindUnconditionalWait|true
+detectorFindTwoLockWait=FindTwoLockWait|true
+detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
+detectorFindUnreleasedLock=FindUnreleasedLock|true
+detectorInefficientIndexOf=InefficientIndexOf|false
+detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
+detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
+detectorOverridingMethodsMustInvokeSuperDetector=OverridingMethodsMustInvokeSuperDetector|true
+detectorWaitInLoop=WaitInLoop|true
+detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true
+detectorBadUseOfReturnValue=BadUseOfReturnValue|true
+detectorFindSqlInjection=FindSqlInjection|true
+detectorUnreadFields=UnreadFields|true
+detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
+detectorFindUselessObjects=FindUselessObjects|true
+detectorBadAppletConstructor=BadAppletConstructor|false
+detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
+detectorSerializableIdiom=SerializableIdiom|true
+detectorNaming=Naming|true
+detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true
+detectorFormatStringChecker=FormatStringChecker|true
+detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
+detectorEmptyZipFileEntry=EmptyZipFileEntry|false
+detectorFindCircularDependencies=FindCircularDependencies|false
+detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
+detectorAtomicityProblem=AtomicityProblem|true
+detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
+detectorInitializationChain=InitializationChain|true
+detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true
+detectorOptionalReturnNull=OptionalReturnNull|true
+detectorStartInConstructor=StartInConstructor|true
+detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
+detectorRedundantConditions=RedundantConditions|true
+effort=default
+detectorRedundantInterfaces=RedundantInterfaces|true
+detectorDuplicateBranches=DuplicateBranches|true
+detectorCheckTypeQualifiers=CheckTypeQualifiers|true
+detectorComparatorIdiom=ComparatorIdiom|true
+detectorFindBadCast2=FindBadCast2|true
+detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
+excludefilter0=findBugs/FindBugsExcludeFilter.xml|true
+detectorBadResultSetAccess=BadResultSetAccess|true
+detectorIncompatMask=IncompatMask|true
+detectorCovariantArrayAssignment=CovariantArrayAssignment|false
+detectorDumbMethodInvocations=DumbMethodInvocations|true
+run_at_full_build=false
+detectorStaticCalendarDetector=StaticCalendarDetector|true
+detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
+detectorVarArgsProblems=VarArgsProblems|true
+detectorInefficientInitializationInsideLoop=InefficientInitializationInsideLoop|false
+detectorCloneIdiom=CloneIdiom|true
+detectorFindHEmismatch=FindHEmismatch|true
+detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
+detectorFindSelfComparison2=FindSelfComparison2|true
+detectorLazyInit=LazyInit|true
+detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
+detectorDontUseEnum=DontUseEnum|true
+detectorFindPuzzlers=FindPuzzlers|true
+detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false
+detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
+detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
+detector_threshold=2
+detectorPublicSemaphores=PublicSemaphores|false
+detectorDumbMethods=DumbMethods|true
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
index 8858c09cbd..a347715d78 100644
--- a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
@@ -8,28 +8,31 @@ Bundle-Localization: plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.apache.sshd.common;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.config.keys;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.file.virtualfs;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.helpers;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.io;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.kex;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.keyprovider;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.session;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.buffer;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.logging;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.security;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.threads;version="[2.4.0,2.5.0)",
- org.apache.sshd.server;version="[2.4.0,2.5.0)",
- org.apache.sshd.server.auth;version="[2.4.0,2.5.0)",
- org.apache.sshd.server.auth.gss;version="[2.4.0,2.5.0)",
- org.apache.sshd.server.auth.keyboard;version="[2.4.0,2.5.0)",
- org.apache.sshd.server.auth.password;version="[2.4.0,2.5.0)",
- org.apache.sshd.server.command;version="[2.4.0,2.5.0)",
- org.apache.sshd.server.session;version="[2.4.0,2.5.0)",
- org.apache.sshd.server.shell;version="[2.4.0,2.5.0)",
- org.apache.sshd.server.subsystem;version="[2.4.0,2.5.0)",
- org.apache.sshd.server.subsystem.sftp;version="[2.4.0,2.5.0)",
+Import-Package: org.apache.sshd.common;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.config.keys;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.file.virtualfs;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.helpers;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.io;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.kex;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.keyprovider;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.session;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.signature;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.buffer;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.logging;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.security;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.threads;version="[2.6.0,2.7.0)",
+ org.apache.sshd.core;version="[2.6.0,2.7.0)",
+ org.apache.sshd.server;version="[2.6.0,2.7.0)",
+ org.apache.sshd.server.auth;version="[2.6.0,2.7.0)",
+ org.apache.sshd.server.auth.gss;version="[2.6.0,2.7.0)",
+ org.apache.sshd.server.auth.keyboard;version="[2.6.0,2.7.0)",
+ org.apache.sshd.server.auth.password;version="[2.6.0,2.7.0)",
+ org.apache.sshd.server.command;version="[2.6.0,2.7.0)",
+ org.apache.sshd.server.session;version="[2.6.0,2.7.0)",
+ org.apache.sshd.server.shell;version="[2.6.0,2.7.0)",
+ org.apache.sshd.server.subsystem;version="[2.6.0,2.7.0)",
+ org.apache.sshd.sftp;version="[2.6.0,2.7.0)",
+ org.apache.sshd.sftp.server;version="[2.6.0,2.7.0)",
org.eclipse.jgit.annotations;version="[6.0.0,6.1.0)",
org.eclipse.jgit.api;version="[6.0.0,6.1.0)",
org.eclipse.jgit.api.errors;version="[6.0.0,6.1.0)",
diff --git a/org.eclipse.jgit.junit.ssh/findBugs/FindBugsExcludeFilter.xml b/org.eclipse.jgit.junit.ssh/findBugs/FindBugsExcludeFilter.xml
new file mode 100644
index 0000000000..999cb71f7c
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/findBugs/FindBugsExcludeFilter.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<FindBugsFilter>
+ <!-- Silence returning null for Boolean return type -->
+ <Match>
+ <Class name="org.eclipse.jgit.junit.ssh.SshTestGitServer$FakeUserAuthGSS" />
+ <Method name="doAuth" />
+ <Bug pattern="NP_BOOLEAN_RETURN_NULL" />
+ </Match>
+</FindBugsFilter>
diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshBasicTestBase.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshBasicTestBase.java
new file mode 100644
index 0000000000..f9ca0b8923
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshBasicTestBase.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.junit.ssh;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.eclipse.jgit.api.Git;
+import org.junit.Test;
+
+/**
+ * Some minimal cloning and fetching tests. Concrete subclasses can implement
+ * the abstract operations from {@link SshTestHarness} to run with different SSH
+ * implementations.
+ */
+public abstract class SshBasicTestBase extends SshTestHarness {
+
+ protected File defaultCloneDir;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ defaultCloneDir = new File(getTemporaryDirectory(), "cloned");
+ }
+
+ @Test
+ public void testSshCloneWithConfig() throws Exception {
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshFetchWithConfig() throws Exception {
+ File localClone = cloneWith("ssh://localhost/doesntmatter",
+ defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ // Do a commit in the upstream repo
+ try (Git git = new Git(db)) {
+ writeTrashFile("SomeOtherFile.txt", "Other commit");
+ git.add().addFilepattern("SomeOtherFile.txt").call();
+ git.commit().setMessage("New commit").call();
+ }
+ // Pull in the clone
+ try (Git git = Git.open(localClone)) {
+ File f = new File(git.getRepository().getWorkTree(),
+ "SomeOtherFile.txt");
+ assertFalse(f.exists());
+ git.pull().setRemote("origin").call();
+ assertTrue(f.exists());
+ assertEquals("Other commit", read(f));
+ }
+ }
+}
diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestBase.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestBase.java
index 3784741195..6fa82f1d68 100644
--- a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestBase.java
+++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestBase.java
@@ -40,7 +40,7 @@ import org.junit.experimental.theories.Theory;
* abstract operations from {@link SshTestHarness}. This gives a way to test
* different ssh clients against a unified test suite.
*/
-public abstract class SshTestBase extends SshTestHarness {
+public abstract class SshTestBase extends SshBasicTestBase {
@DataPoints
public static String[] KEY_RESOURCES = { //
@@ -65,14 +65,6 @@ public abstract class SshTestBase extends SshTestHarness {
"id_ed25519_testpass", //
"id_ed25519_expensive_testpass" };
- protected File defaultCloneDir;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- defaultCloneDir = new File(getTemporaryDirectory(), "cloned");
- }
-
@Test
public void testSshWithoutConfig() throws Exception {
assertThrows(TransportException.class,
@@ -133,16 +125,6 @@ public abstract class SshTestBase extends SshTestHarness {
}
@Test
- public void testSshWithConfig() throws Exception {
- cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
- "Host localhost", //
- "HostName localhost", //
- "Port " + testPort, //
- "User " + TEST_USER, //
- "IdentityFile " + privateKey1.getAbsolutePath());
- }
-
- @Test
public void testSshWithConfigEncryptedUnusedKey() throws Exception {
// Copy the encrypted test key from the bundle.
File encryptedKey = new File(sshDir, "id_dsa");
diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java
index ab8e0c1ca0..4fe98f8683 100644
--- a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java
+++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java
@@ -9,6 +9,9 @@
*/
package org.eclipse.jgit.junit.ssh;
+import static org.apache.sshd.core.CoreModuleProperties.SERVER_EXTRA_IDENTIFICATION_LINES;
+import static org.apache.sshd.core.CoreModuleProperties.SERVER_EXTRA_IDENT_LINES_SEPARATOR;
+
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -21,26 +24,28 @@ import java.security.KeyPair;
import java.security.PublicKey;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
+import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.PropertyResolver;
-import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
-import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.signature.Signature;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.apache.sshd.common.util.threads.CloseableExecutorService;
import org.apache.sshd.common.util.threads.ThreadUtils;
import org.apache.sshd.server.ServerAuthenticationManager;
-import org.apache.sshd.server.ServerFactoryManager;
+import org.apache.sshd.server.ServerBuilder;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.auth.UserAuth;
import org.apache.sshd.server.auth.UserAuthFactory;
@@ -52,8 +57,9 @@ import org.apache.sshd.server.command.AbstractCommandSupport;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.shell.UnknownCommand;
import org.apache.sshd.server.subsystem.SubsystemFactory;
-import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
+import org.apache.sshd.sftp.server.SftpSubsystemFactory;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.RemoteConfig;
@@ -161,7 +167,9 @@ public class SshTestGitServer {
this.testUser = testUser;
setTestUserPublicKey(testKey);
this.repository = repository;
- server = SshServer.setUpDefaultServer();
+ ServerBuilder builder = ServerBuilder.builder()
+ .signatureFactories(getSignatureFactories());
+ server = builder.build();
hostKeys.add(hostKey);
server.setKeyPairProvider((session) -> hostKeys);
@@ -186,6 +194,37 @@ public class SshTestGitServer {
});
}
+ /**
+ * Apache MINA sshd 2.6.0 has removed DSA, DSA_CERT and RSA_CERT. We have to
+ * set it up explicitly to still allow users to connect with DSA keys.
+ *
+ * @return a list of supported signature factories
+ */
+ @SuppressWarnings("deprecation")
+ private static List<NamedFactory<Signature>> getSignatureFactories() {
+ // @formatter:off
+ return Arrays.asList(
+ BuiltinSignatures.nistp256_cert,
+ BuiltinSignatures.nistp384_cert,
+ BuiltinSignatures.nistp521_cert,
+ BuiltinSignatures.ed25519_cert,
+ BuiltinSignatures.rsaSHA512_cert,
+ BuiltinSignatures.rsaSHA256_cert,
+ BuiltinSignatures.rsa_cert,
+ BuiltinSignatures.nistp256,
+ BuiltinSignatures.nistp384,
+ BuiltinSignatures.nistp521,
+ BuiltinSignatures.ed25519,
+ BuiltinSignatures.sk_ecdsa_sha2_nistp256,
+ BuiltinSignatures.sk_ssh_ed25519,
+ BuiltinSignatures.rsaSHA512,
+ BuiltinSignatures.rsaSHA256,
+ BuiltinSignatures.rsa,
+ BuiltinSignatures.dsa_cert,
+ BuiltinSignatures.dsa);
+ // @formatter:on
+ }
+
private static PublicKey readPublicKey(Path key)
throws IOException, GeneralSecurityException {
return AuthorizedKeyEntry.readAuthorizedKeys(key).get(0)
@@ -202,7 +241,7 @@ public class SshTestGitServer {
private static class FakeUserAuthGSS extends UserAuthGSS {
@Override
- protected Boolean doAuth(Buffer buffer, boolean initial)
+ protected @Nullable Boolean doAuth(Buffer buffer, boolean initial)
throws Exception {
// We always reply that we did do this, but then we fail at the
// first token message. That way we can test that the client-side
@@ -277,14 +316,8 @@ public class SshTestGitServer {
@NonNull
protected List<SubsystemFactory> configureSubsystems() {
// SFTP.
- server.setFileSystemFactory(new VirtualFileSystemFactory() {
-
- @Override
- protected Path computeRootDir(Session session) throws IOException {
- return SshTestGitServer.this.repository.getDirectory()
- .getParentFile().getAbsoluteFile().toPath();
- }
- });
+ server.setFileSystemFactory(new VirtualFileSystemFactory(repository
+ .getDirectory().getParentFile().getAbsoluteFile().toPath()));
return Collections
.singletonList((new SftpSubsystemFactory.Builder()).build());
}
@@ -433,9 +466,8 @@ public class SshTestGitServer {
*/
public void setPreamble(String... lines) {
if (lines != null && lines.length > 0) {
- PropertyResolverUtils.updateProperty(this.server,
- ServerFactoryManager.SERVER_EXTRA_IDENTIFICATION_LINES,
- String.join("|", lines));
+ SERVER_EXTRA_IDENTIFICATION_LINES.set(server, String.join(
+ String.valueOf(SERVER_EXTRA_IDENT_LINES_SEPARATOR), lines));
}
}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SeparateClassloaderTestRunner.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SeparateClassloaderTestRunner.java
index b982787e75..4a4dc92c43 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SeparateClassloaderTestRunner.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SeparateClassloaderTestRunner.java
@@ -9,10 +9,10 @@
*/
package org.eclipse.jgit.junit;
-import static java.lang.ClassLoader.getSystemClassLoader;
-
+import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
+import java.nio.file.Paths;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;
@@ -40,7 +40,13 @@ public class SeparateClassloaderTestRunner extends BlockJUnit4ClassRunner {
private static Class<?> loadNewClass(Class<?> klass)
throws InitializationError {
try {
- URL[] urls = ((URLClassLoader) getSystemClassLoader()).getURLs();
+ String pathSeparator = System.getProperty("path.separator");
+ String[] classPathEntries = System.getProperty("java.class.path")
+ .split(pathSeparator);
+ URL[] urls = new URL[classPathEntries.length];
+ for (int i = 0; i < classPathEntries.length; i++) {
+ urls[i] = Paths.get(classPathEntries[i]).toUri().toURL();
+ }
ClassLoader testClassLoader = new URLClassLoader(urls) {
@Override
@@ -54,7 +60,7 @@ public class SeparateClassloaderTestRunner extends BlockJUnit4ClassRunner {
}
};
return Class.forName(klass.getName(), true, testClassLoader);
- } catch (ClassNotFoundException e) {
+ } catch (ClassNotFoundException | MalformedURLException e) {
throw new InitializationError(e);
}
}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
index a5b3b1f3ac..e3eb2c5367 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -43,7 +43,7 @@ import org.eclipse.jgit.errors.ObjectWritingException;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.internal.storage.file.LockFile;
import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
-import org.eclipse.jgit.internal.storage.file.PackFile;
+import org.eclipse.jgit.internal.storage.file.Pack;
import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
import org.eclipse.jgit.lib.AnyObjectId;
@@ -773,7 +773,7 @@ public class TestRepository<R extends Repository> implements AutoCloseable {
rw.writeInfoRefs();
final StringBuilder w = new StringBuilder();
- for (PackFile p : fr.getObjectDatabase().getPacks()) {
+ for (Pack p : fr.getObjectDatabase().getPacks()) {
w.append("P ");
w.append(p.getPackFile().getName());
w.append('\n');
@@ -954,7 +954,7 @@ public class TestRepository<R extends Repository> implements AutoCloseable {
}
private static void prunePacked(ObjectDirectory odb) throws IOException {
- for (PackFile p : odb.getPacks()) {
+ for (Pack p : odb.getPacks()) {
for (MutableEntry e : p)
FileUtils.delete(odb.fileFor(e.toObjectId()));
}
diff --git a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
index 349c3b3712..c44152937a 100644
--- a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
@@ -9,11 +9,11 @@ Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: javax.servlet;version="[3.1.0,4.0.0)",
javax.servlet.http;version="[3.1.0,4.0.0)",
- org.apache.http;version="[4.3.0,5.0.0)",
- org.apache.http.client;version="[4.3.0,5.0.0)",
- org.apache.http.client.methods;version="[4.3.0,5.0.0)",
- org.apache.http.entity;version="[4.3.0,5.0.0)",
- org.apache.http.impl.client;version="[4.3.0,5.0.0)",
+ org.apache.http;version="[4.4.0,5.0.0)",
+ org.apache.http.client;version="[4.4.0,5.0.0)",
+ org.apache.http.client.methods;version="[4.4.0,5.0.0)",
+ org.apache.http.entity;version="[4.4.0,5.0.0)",
+ org.apache.http.impl.client;version="[4.4.0,5.0.0)",
org.eclipse.jetty.continuation;version="[9.4.5,10.0.0)",
org.eclipse.jetty.http;version="[9.4.5,10.0.0)",
org.eclipse.jetty.io;version="[9.4.5,10.0.0)",
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
index df115dba0a..76050e117a 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
@@ -24,7 +24,6 @@ Import-Package: com.google.gson;version="[2.8.0,3.0.0)",
javax.servlet.annotation;version="[3.1.0,4.0.0)",
javax.servlet.http;version="[3.1.0,4.0.0)",
org.apache.http;version="[4.3.0,5.0.0)",
- org.apache.http.client;version="[4.3.0,5.0.0)",
org.eclipse.jgit.annotations;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.storage.file;version="[6.0.0,6.1.0)",
diff --git a/org.eclipse.jgit.lfs.server/findBugs/FindBugsExcludeFilter.xml b/org.eclipse.jgit.lfs.server/findBugs/FindBugsExcludeFilter.xml
new file mode 100644
index 0000000000..c5f3f80139
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/findBugs/FindBugsExcludeFilter.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<FindBugsFilter>
+<!-- Field is written by gson, seems like spotbugs doesn't recognize this -->
+ <Match>
+ <Class name="org.eclipse.jgit.lfs.server.LfsObject" />
+ <Bug pattern="UWF_UNWRITTEN_FIELD" />
+ </Match>
+</FindBugsFilter>
diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
index 3dd5840397..b853c6a7ed 100644
--- a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
@@ -51,8 +51,8 @@ org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java
index 7ee898fab2..da78b28d90 100644
--- a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java
+++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java
@@ -12,7 +12,13 @@ package org.eclipse.jgit.lfs.lib;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -23,17 +29,275 @@ import org.junit.Test;
* Test LfsPointer file abstraction
*/
public class LFSPointerTest {
+
+ private static final String TEST_SHA256 = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+
@Test
public void testEncoding() throws IOException {
- final String s = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
- AnyLongObjectId id = LongObjectId.fromString(s);
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
LfsPointer ptr = new LfsPointer(id, 4);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
ptr.encode(baos);
assertEquals(
"version https://git-lfs.github.com/spec/v1\noid sha256:"
- + s + "\nsize 4\n",
+ + TEST_SHA256 + "\nsize 4\n",
baos.toString(UTF_8.name()));
}
}
+
+ @Test
+ public void testReadValidLfsPointer() throws Exception {
+ String ptr = "version https://git-lfs.github.com/spec/v1\n"
+ + "oid sha256:" + TEST_SHA256 + '\n'
+ + "size 4\n";
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ try (ByteArrayInputStream in = new ByteArrayInputStream(
+ ptr.getBytes(UTF_8))) {
+ assertEquals(lfs, LfsPointer.parseLfsPointer(in));
+ }
+ }
+
+ @Test
+ public void testReadValidLfsPointerUnordered() throws Exception {
+ // This is actually not allowed per the spec, but JGit accepts it
+ // anyway.
+ String ptr = "version https://git-lfs.github.com/spec/v1\n"
+ + "size 4\n"
+ + "oid sha256:" + TEST_SHA256 + '\n';
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ try (ByteArrayInputStream in = new ByteArrayInputStream(
+ ptr.getBytes(UTF_8))) {
+ assertEquals(lfs, LfsPointer.parseLfsPointer(in));
+ }
+ }
+
+ @Test
+ public void testReadValidLfsPointerVersionNotFirst() throws Exception {
+ // This is actually not allowed per the spec, but JGit accepts it
+ // anyway.
+ String ptr = "oid sha256:" + TEST_SHA256 + '\n'
+ + "size 4\n"
+ + "version https://git-lfs.github.com/spec/v1\n";
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ try (ByteArrayInputStream in = new ByteArrayInputStream(
+ ptr.getBytes(UTF_8))) {
+ assertEquals(lfs, LfsPointer.parseLfsPointer(in));
+ }
+ }
+
+ @Test
+ public void testReadInvalidLfsPointer() throws Exception {
+ String cSource = "size_t someFunction(void *ptr); // Fake C source\n";
+ try (ByteArrayInputStream in = new ByteArrayInputStream(
+ cSource.getBytes(UTF_8))) {
+ assertNull("Is not a LFS pointer", LfsPointer.parseLfsPointer(in));
+ }
+ }
+
+ @Test
+ public void testReadInvalidLfsPointer2() throws Exception {
+ String cSource = "size_t\nsomeFunction(void *ptr);\n// Fake C source\n";
+ try (ByteArrayInputStream in = new ByteArrayInputStream(
+ cSource.getBytes(UTF_8))) {
+ assertNull("Is not a LFS pointer", LfsPointer.parseLfsPointer(in));
+ }
+ }
+
+ @Test
+ public void testReadInValidLfsPointerVersionWrong() throws Exception {
+ String ptr = "version https://git-lfs.example.org/spec/v1\n"
+ + "oid sha256:" + TEST_SHA256 + '\n'
+ + "size 4\n";
+ try (ByteArrayInputStream in = new ByteArrayInputStream(
+ ptr.getBytes(UTF_8))) {
+ assertNull("Is not a LFS pointer", LfsPointer.parseLfsPointer(in));
+ }
+ }
+
+ @Test
+ public void testReadInValidLfsPointerVersionTwice() throws Exception {
+ String ptr = "version https://git-lfs.github.com/spec/v1\n"
+ + "version https://git-lfs.github.com/spec/v1\n"
+ + "oid sha256:" + TEST_SHA256 + '\n'
+ + "size 4\n";
+ try (ByteArrayInputStream in = new ByteArrayInputStream(
+ ptr.getBytes(UTF_8))) {
+ assertNull("Is not a LFS pointer", LfsPointer.parseLfsPointer(in));
+ }
+ }
+
+ @Test
+ public void testReadInValidLfsPointerVersionTwice2() throws Exception {
+ String ptr = "version https://git-lfs.github.com/spec/v1\n"
+ + "oid sha256:" + TEST_SHA256 + '\n'
+ + "version https://git-lfs.github.com/spec/v1\n"
+ + "size 4\n";
+ try (ByteArrayInputStream in = new ByteArrayInputStream(
+ ptr.getBytes(UTF_8))) {
+ assertNull("Is not a LFS pointer", LfsPointer.parseLfsPointer(in));
+ }
+ }
+
+ @Test
+ public void testReadInValidLfsPointerOidTwice() throws Exception {
+ String ptr = "version https://git-lfs.github.com/spec/v1\n"
+ + "oid sha256:" + TEST_SHA256 + '\n'
+ + "oid sha256:" + TEST_SHA256 + '\n'
+ + "size 4\n";
+ try (ByteArrayInputStream in = new ByteArrayInputStream(
+ ptr.getBytes(UTF_8))) {
+ assertNull("Is not a LFS pointer", LfsPointer.parseLfsPointer(in));
+ }
+ }
+
+ @Test
+ public void testReadInValidLfsPointerSizeTwice() throws Exception {
+ String ptr = "version https://git-lfs.github.com/spec/v1\n"
+ + "size 4\n"
+ + "size 4\n"
+ + "oid sha256:" + TEST_SHA256 + '\n';
+ try (ByteArrayInputStream in = new ByteArrayInputStream(
+ ptr.getBytes(UTF_8))) {
+ assertNull("Is not a LFS pointer", LfsPointer.parseLfsPointer(in));
+ }
+ }
+
+ @Test
+ public void testRoundtrip() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer ptr = new LfsPointer(id, 4);
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ ptr.encode(baos);
+ try (ByteArrayInputStream in = new ByteArrayInputStream(
+ baos.toByteArray())) {
+ assertEquals(ptr, LfsPointer.parseLfsPointer(in));
+ }
+ }
+ }
+
+ @Test
+ public void testEquals() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ AnyLongObjectId id2 = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs2 = new LfsPointer(id2, 4);
+ assertTrue(lfs.equals(lfs2));
+ assertTrue(lfs2.equals(lfs));
+ }
+
+ @Test
+ public void testEqualsNull() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ assertFalse(lfs.equals(null));
+ }
+
+ @Test
+ public void testEqualsSame() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ assertTrue(lfs.equals(lfs));
+ }
+
+ @Test
+ public void testEqualsOther() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ assertFalse(lfs.equals(new Object()));
+ }
+
+ @Test
+ public void testNotEqualsOid() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ AnyLongObjectId id2 = LongObjectId
+ .fromString(TEST_SHA256.replace('7', '5'));
+ LfsPointer lfs2 = new LfsPointer(id2, 4);
+ assertFalse(lfs.equals(lfs2));
+ assertFalse(lfs2.equals(lfs));
+ }
+
+ @Test
+ public void testNotEqualsSize() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ AnyLongObjectId id2 = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs2 = new LfsPointer(id2, 5);
+ assertFalse(lfs.equals(lfs2));
+ assertFalse(lfs2.equals(lfs));
+ }
+
+ @Test
+ public void testCompareToEquals() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ AnyLongObjectId id2 = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs2 = new LfsPointer(id2, 4);
+ assertEquals(0, lfs.compareTo(lfs2));
+ assertEquals(0, lfs2.compareTo(lfs));
+ }
+
+ @Test
+ @SuppressWarnings("SelfComparison")
+ public void testCompareToSame() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ assertEquals(0, lfs.compareTo(lfs));
+ }
+
+ @Test
+ public void testCompareToNotEqualsOid() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ AnyLongObjectId id2 = LongObjectId
+ .fromString(TEST_SHA256.replace('7', '5'));
+ LfsPointer lfs2 = new LfsPointer(id2, 4);
+ assertNotEquals(0, lfs.compareTo(lfs2));
+ assertNotEquals(0, lfs2.compareTo(lfs));
+ }
+
+ @Test
+ public void testCompareToNotEqualsSize() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ AnyLongObjectId id2 = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs2 = new LfsPointer(id2, 5);
+ assertNotEquals(0, lfs.compareTo(lfs2));
+ assertNotEquals(0, lfs2.compareTo(lfs));
+ }
+
+ @Test
+ public void testCompareToNull() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ assertThrows(NullPointerException.class, () -> lfs.compareTo(null));
+ }
+
+ @Test
+ public void testHashcodeEquals() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ AnyLongObjectId id2 = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs2 = new LfsPointer(id2, 4);
+ assertEquals(lfs.hashCode(), lfs2.hashCode());
+ }
+
+ @Test
+ public void testHashcodeSame() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ assertEquals(lfs.hashCode(), lfs.hashCode());
+ }
+
+ @Test
+ public void testHashcodeNotEquals() throws Exception {
+ AnyLongObjectId id = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs = new LfsPointer(id, 4);
+ AnyLongObjectId id2 = LongObjectId.fromString(TEST_SHA256);
+ LfsPointer lfs2 = new LfsPointer(id2, 5);
+ assertNotEquals(lfs.hashCode(), lfs2.hashCode());
+ }
}
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
index 0276088312..3eb5a7671a 100644
--- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -13,8 +13,6 @@ Export-Package: org.eclipse.jgit.lfs;version="6.0.0",
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.google.gson;version="[2.8.2,3.0.0)",
com.google.gson.stream;version="[2.8.2,3.0.0)",
- org.apache.http.impl.client;version="[4.2.6,5.0.0)",
- org.apache.http.impl.conn;version="[4.2.6,5.0.0)",
org.eclipse.jgit.annotations;version="[6.0.0,6.1.0)";resolution:=optional,
org.eclipse.jgit.api.errors;version="[6.0.0,6.1.0)",
org.eclipse.jgit.attributes;version="[6.0.0,6.1.0)",
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java
index 4e2d8a998d..aef4416387 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016, Christian Halstrick <christian.halstrick@sap.com> and others
+ * Copyright (C) 2016, 2021 Christian Halstrick <christian.halstrick@sap.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -19,6 +19,7 @@ import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.Locale;
+import java.util.Objects;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
@@ -56,9 +57,9 @@ public class LfsPointer implements Comparable<LfsPointer> {
public static final String HASH_FUNCTION_NAME = Constants.LONG_HASH_FUNCTION
.toLowerCase(Locale.ROOT).replace("-", ""); //$NON-NLS-1$ //$NON-NLS-2$
- private AnyLongObjectId oid;
+ private final AnyLongObjectId oid;
- private long size;
+ private final long size;
/**
* <p>Constructor for LfsPointer.</p>
@@ -129,19 +130,49 @@ public class LfsPointer implements Comparable<LfsPointer> {
LongObjectId id = null;
long sz = -1;
+ // This parsing is a bit too general if we go by the spec at
+ // https://github.com/git-lfs/git-lfs/blob/master/docs/spec.md
+ // Comment lines are not mentioned in the spec, and the "version" line
+ // MUST be the first.
try (BufferedReader br = new BufferedReader(
new InputStreamReader(in, UTF_8))) {
for (String s = br.readLine(); s != null; s = br.readLine()) {
if (s.startsWith("#") || s.length() == 0) { //$NON-NLS-1$
continue;
- } else if (s.startsWith("version") && s.length() > 8 //$NON-NLS-1$
- && (s.substring(8).trim().equals(VERSION) ||
- s.substring(8).trim().equals(VERSION_LEGACY))) {
- versionLine = true;
- } else if (s.startsWith("oid sha256:")) { //$NON-NLS-1$
- id = LongObjectId.fromString(s.substring(11).trim());
- } else if (s.startsWith("size") && s.length() > 5) { //$NON-NLS-1$
- sz = Long.parseLong(s.substring(5).trim());
+ } else if (s.startsWith("version")) { //$NON-NLS-1$
+ if (versionLine || s.length() < 8 || s.charAt(7) != ' ') {
+ return null; // Not a LFS pointer
+ }
+ String rest = s.substring(8).trim();
+ versionLine = VERSION.equals(rest)
+ || VERSION_LEGACY.equals(rest);
+ if (!versionLine) {
+ return null; // Not a LFS pointer
+ }
+ } else {
+ try {
+ if (s.startsWith("oid sha256:")) { //$NON-NLS-1$
+ if (id != null) {
+ return null; // Not a LFS pointer
+ }
+ id = LongObjectId
+ .fromString(s.substring(11).trim());
+ } else if (s.startsWith("size")) { //$NON-NLS-1$
+ if (sz > 0 || s.length() < 5
+ || s.charAt(4) != ' ') {
+ return null; // Not a LFS pointer
+ }
+ sz = Long.parseLong(s.substring(5).trim());
+ }
+ } catch (RuntimeException e) {
+ // We could not parse the line. If we have a version
+ // already, this is a corrupt LFS pointer. Otherwise it
+ // is just not an LFS pointer.
+ if (versionLine) {
+ throw e;
+ }
+ return null;
+ }
}
}
if (versionLine && id != null && sz > -1) {
@@ -170,5 +201,22 @@ public class LfsPointer implements Comparable<LfsPointer> {
return Long.compare(getSize(), o.getSize());
}
-}
+ @Override
+ public int hashCode() {
+ return Objects.hash(getOid()) * 31 + Long.hashCode(getSize());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ LfsPointer other = (LfsPointer) obj;
+ return Objects.equals(getOid(), other.getOid())
+ && getSize() == other.getSize();
+ }
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java
index 7a0ed456c1..e221913bea 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java
@@ -18,7 +18,9 @@ import java.io.IOException;
import java.net.ProxySelector;
import java.net.URISyntaxException;
import java.net.URL;
-import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
@@ -258,8 +260,8 @@ public class LfsConnectionFactory {
private static final class AuthCache {
private static final long AUTH_CACHE_EAGER_TIMEOUT = 500;
- private static final SimpleDateFormat ISO_FORMAT = new SimpleDateFormat(
- "yyyy-MM-dd'T'HH:mm:ss.SSSX"); //$NON-NLS-1$
+ private static final DateTimeFormatter ISO_FORMAT = DateTimeFormatter
+ .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX"); //$NON-NLS-1$
/**
* Creates a cache entry for an authentication response.
@@ -278,8 +280,10 @@ public class LfsConnectionFactory {
- AUTH_CACHE_EAGER_TIMEOUT;
} else if (action.expiresAt != null
&& !action.expiresAt.isEmpty()) {
- this.validUntil = ISO_FORMAT.parse(action.expiresAt)
- .getTime() - AUTH_CACHE_EAGER_TIMEOUT;
+ this.validUntil = LocalDateTime
+ .parse(action.expiresAt, ISO_FORMAT)
+ .atZone(ZoneOffset.UTC).toInstant().toEpochMilli()
+ - AUTH_CACHE_EAGER_TIMEOUT;
} else {
this.validUntil = System.currentTimeMillis();
}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
index 312c62f571..526e8b331e 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
@@ -36,25 +36,4 @@
version="0.0.0"
unpack="false"/>
- <plugin
- id="javaewah"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
- <plugin
- id="org.apache.commons.compress"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
- <plugin
- id="org.slf4j.api"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml
index 0c7d0bb8d2..6883afc349 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml
@@ -34,25 +34,4 @@
fragment="true"
unpack="false"/>
- <plugin
- id="org.bouncycastle.bcpg"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
- <plugin
- id="org.bouncycastle.bcpkix"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
- <plugin
- id="org.bouncycastle.bcprov"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
index 8e7bef4b7f..22f995e8d8 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
@@ -33,25 +33,4 @@
version="0.0.0"
unpack="false"/>
- <plugin
- id="org.apache.httpcomponents.httpcore"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
- <plugin
- id="org.apache.httpcomponents.httpclient"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
- <plugin
- id="org.apache.commons.codec"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
index 19360b4dfc..4b5032e45b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
@@ -40,11 +40,4 @@
version="0.0.0"
unpack="false"/>
- <plugin
- id="com.google.gson"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
index 10529823e9..d97ce7552e 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
@@ -54,25 +54,4 @@
version="0.0.0"
unpack="false"/>
- <plugin
- id="org.apache.commons.compress"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
- <plugin
- id="org.kohsuke.args4j"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
- <plugin
- id="javaewah"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
index abda695639..a56cf0a1f2 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
@@ -39,6 +39,138 @@
<bundle id="org.eclipse.jgit.ui" version="0.0.0">
<category name="JGit-additional-bundles"/>
</bundle>
+ <bundle id="com.google.gson">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="com.google.gson.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="com.jcraft.jsch">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="com.jcraft.jsch.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="com.jcraft.jzlib">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="com.jcraft.jzlib.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="javaewah">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="javaewah.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="javax.servlet">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="javax.servlet.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="net.i2p.crypto.eddsa">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="net.i2p.crypto.eddsa.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.ant">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.ant.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.commons.codec">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.commons.codec.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.commons.compress">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.commons.compress.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.commons.logging">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.commons.logging.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.httpcomponents.httpclient">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.httpcomponents.httpclient.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.httpcomponents.httpcore">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.httpcomponents.httpcore.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.log4j">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.log4j.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.sshd.osgi">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.sshd.osgi.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.sshd.sftp">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.sshd.sftp.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.bouncycastle.bcpg">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.bouncycastle.bcpg.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.bouncycastle.bcpkix">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.bouncycastle.bcpkix.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.bouncycastle.bcprov">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.bouncycastle.bcprov.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.kohsuke.args4j">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.kohsuke.args4j.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.slf4j.api">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.slf4j.api.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.slf4j.binding.log4j12">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.slf4j.binding.log4j12.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.tukaani.xz">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.tukaani.xz.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
<category-def name="JGit" label="Java implementation of Git">
<description>
Java implementation of Git
@@ -49,4 +181,5 @@
Java implementation of Git - additional bundles
</description>
</category-def>
+ <category-def name="JGit-dependency-bundles" label="JGit dependency bundles"/>
</site>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml
index 4efafc2f30..46ea77995c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml
@@ -33,25 +33,4 @@
version="0.0.0"
unpack="false"/>
- <plugin
- id="org.apache.sshd.osgi"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
- <plugin
- id="org.apache.sshd.sftp"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
- <plugin
- id="net.i2p.crypto.eddsa"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml
index 50ef7c30a2..06a6ed91ee 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml
@@ -34,18 +34,4 @@
fragment="true"
unpack="false"/>
- <plugin
- id="com.jcraft.jsch"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
- <plugin
- id="com.jcraft.jzlib"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
-
</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target
index 931e196cf6..68378a2b0b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.10" sequenceNumber="1605866255">
+<target name="jgit-4.10" sequenceNumber="1613861945">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,7 +86,7 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd
index b87917b8ab..fb1ac6b255 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd
@@ -1,7 +1,7 @@
target "jgit-4.10" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
+include "orbit/S20210216215844.tpd"
location "https://download.eclipse.org/releases/2018-12/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
index 8090999601..18d525d8ef 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.11" sequenceNumber="1605866333">
+<target name="jgit-4.11" sequenceNumber="1613862033">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,7 +86,7 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd
index 844a1d791f..0d56280631 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd
@@ -1,7 +1,7 @@
target "jgit-4.11" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
+include "orbit/S20210216215844.tpd"
location "https://download.eclipse.org/releases/2019-03/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target
index 69eb639726..d72f08d29d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.12" sequenceNumber="1605866333">
+<target name="jgit-4.12" sequenceNumber="1613862033">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,7 +86,7 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.tpd
index afbf79d531..5a024152d4 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.tpd
@@ -1,7 +1,7 @@
target "jgit-4.12" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
+include "orbit/S20210216215844.tpd"
location "https://download.eclipse.org/releases/2019-06/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target
index fdbed2d2a4..d0e559271c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.13" sequenceNumber="1605866333">
+<target name="jgit-4.13" sequenceNumber="1613862034">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,7 +86,7 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.tpd
index d83e338460..84e5c25efb 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.tpd
@@ -1,7 +1,7 @@
target "jgit-4.13" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
+include "orbit/S20210216215844.tpd"
location "https://download.eclipse.org/releases/2019-09/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target
index c2ef9d24a1..42278f6efd 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.14" sequenceNumber="1605866331">
+<target name="jgit-4.14" sequenceNumber="1613862030">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,7 +86,7 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.tpd
index f357ccd24c..6d793a607b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.tpd
@@ -1,7 +1,7 @@
target "jgit-4.14" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
+include "orbit/S20210216215844.tpd"
location "https://download.eclipse.org/releases/2019-12/201912181000/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target
index 4034d2a362..0d5166e16f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.15" sequenceNumber="1605866331">
+<target name="jgit-4.15" sequenceNumber="1613862030">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,7 +86,7 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.tpd
index 881fe37e73..4ce832bf98 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.tpd
@@ -1,7 +1,7 @@
target "jgit-4.15" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
+include "orbit/S20210216215844.tpd"
location "https://download.eclipse.org/releases/2020-03/202003181000/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target
index 34d872cdd5..b4d53069b7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.16" sequenceNumber="1605866333">
+<target name="jgit-4.16" sequenceNumber="1613862033">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,7 +86,7 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.tpd
index 9a07597119..1b56447ce3 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.tpd
@@ -1,7 +1,7 @@
target "jgit-4.16" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
+include "orbit/S20210216215844.tpd"
location "https://download.eclipse.org/releases/2020-06/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
index 3384c23563..47fc74be64 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.17" sequenceNumber="1605866541">
+<target name="jgit-4.17" sequenceNumber="1613862034">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,7 +86,7 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
index ce79cf45e8..367020ce0f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
@@ -1,7 +1,7 @@
target "jgit-4.17" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
+include "orbit/S20210216215844.tpd"
location "https://download.eclipse.org/releases/2020-09/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.tpd
deleted file mode 100644
index 0669490bb0..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.18-staging" with source configurePhase
-
-include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
-
-location "https://download.eclipse.org/staging/2020-12/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
new file mode 100644
index 0000000000..b393e60752
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.18" sequenceNumber="1613862034">
+ <locations>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
+ </location>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
+ <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
+ <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
+ <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
+ <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
+ <unit id="javaewah" version="1.1.7.v20200107-0831"/>
+ <unit id="javaewah.source" version="1.1.7.v20200107-0831"/>
+ <unit id="javax.servlet" version="3.1.0.v201410161800"/>
+ <unit id="javax.servlet.source" version="3.1.0.v201410161800"/>
+ <unit id="net.bytebuddy.byte-buddy" version="1.9.0.v20181107-1410"/>
+ <unit id="net.bytebuddy.byte-buddy-agent" version="1.9.0.v20181106-1534"/>
+ <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.9.0.v20181106-1534"/>
+ <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/>
+ <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
+ <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
+ <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+ <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
+ <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
+ <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
+ <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
+ <unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
+ <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
+ <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
+ <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.assertj" version="3.14.0.v20200120-1926"/>
+ <unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
+ <unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
+ <unit id="org.bouncycastle.bcpg.source" version="1.65.0.v20200527-1955"/>
+ <unit id="org.bouncycastle.bcpkix" version="1.65.0.v20200527-1955"/>
+ <unit id="org.bouncycastle.bcpkix.source" version="1.65.0.v20200527-1955"/>
+ <unit id="org.bouncycastle.bcprov" version="1.65.1.v20200529-1514"/>
+ <unit id="org.bouncycastle.bcprov.source" version="1.65.1.v20200529-1514"/>
+ <unit id="org.hamcrest" version="1.1.0.v20090501071000"/>
+ <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
+ <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
+ <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/>
+ <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/>
+ <unit id="org.junit" version="4.13.0.v20200204-1500"/>
+ <unit id="org.junit.source" version="4.13.0.v20200204-1500"/>
+ <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/>
+ <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/>
+ <unit id="org.mockito" version="2.23.0.v20200310-1642"/>
+ <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
+ <unit id="org.objenesis" version="2.6.0.v20180420-1519"/>
+ <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/>
+ <unit id="org.slf4j.api" version="1.7.30.v20200204-2150"/>
+ <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+ <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+ <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
+ <unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
+ <unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
+ </location>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="org.eclipse.osgi" version="0.0.0"/>
+ <repository location="https://download.eclipse.org/releases/2020-12/"/>
+ </location>
+ </locations>
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd
new file mode 100644
index 0000000000..507ddd1dc1
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.18" with source configurePhase
+
+include "projects/jetty-9.4.x.tpd"
+include "orbit/S20210216215844.tpd"
+
+location "https://download.eclipse.org/releases/2020-12/" {
+ org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19-staging.target
index 5eeab9534a..f376926164 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19-staging.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.18-staging" sequenceNumber="1605866541">
+<target name="jgit-4.19-staging" sequenceNumber="1613862034">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,11 +86,11 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/staging/2020-12/"/>
+ <repository location="https://download.eclipse.org/staging/2021-03/"/>
</location>
</locations>
</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19-staging.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19-staging.tpd
new file mode 100644
index 0000000000..3b1b19c846
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19-staging.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.19-staging" with source configurePhase
+
+include "projects/jetty-9.4.x.tpd"
+include "orbit/S20210216215844.tpd"
+
+location "https://download.eclipse.org/staging/2021-03/" {
+ org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
index a66fcc0821..26715ee181 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.6" sequenceNumber="1605866347">
+<target name="jgit-4.6" sequenceNumber="1613862049">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,7 +86,7 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
index aa58b68577..23bf87c076 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
@@ -1,7 +1,7 @@
target "jgit-4.6" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
+include "orbit/S20210216215844.tpd"
location "https://download.eclipse.org/releases/neon/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
index 4b5410a3d7..64fe054953 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.7" sequenceNumber="1605866338">
+<target name="jgit-4.7" sequenceNumber="1613862039">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,7 +86,7 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd
index e2264e0038..c33e4a39b1 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd
@@ -1,7 +1,7 @@
target "jgit-4.7" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
+include "orbit/S20210216215844.tpd"
location "https://download.eclipse.org/releases/oxygen/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
index d776427455..f7a3a3b26f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.8" sequenceNumber="1605866333">
+<target name="jgit-4.8" sequenceNumber="1613862034">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,7 +86,7 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd
index c92ce53963..c40bacdb87 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd
@@ -1,7 +1,7 @@
target "jgit-4.8" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
+include "orbit/S20210216215844.tpd"
location "https://download.eclipse.org/releases/photon/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
index 56002b78a8..4afbe99738 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
@@ -1,30 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.9" sequenceNumber="1605866333">
+<target name="jgit-4.9" sequenceNumber="1613862033">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.client.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.continuation.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.http.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.io.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.security.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.server.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.servlet.source" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util" version="9.4.30.v20200611"/>
- <unit id="org.eclipse.jetty.util.source" version="9.4.30.v20200611"/>
- <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/"/>
+ <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+ <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+ <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
- <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
+ <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
+ <unit id="com.google.gson.source" version="2.8.6.v20201231-1626"/>
<unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
<unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
@@ -47,16 +49,16 @@
<unit id="org.apache.commons.compress.source" version="1.19.0.v20200106-2343"/>
<unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
<unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.10.v20200830-2311"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.12.v20200108-1212"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.12.v20200108-1212"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
- <unit id="org.apache.sshd.osgi" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.osgi.source" version="2.4.0.v20200318-1614"/>
- <unit id="org.apache.sshd.sftp" version="2.4.0.v20200319-1547"/>
- <unit id="org.apache.sshd.sftp.source" version="2.4.0.v20200319-1547"/>
+ <unit id="org.apache.sshd.osgi" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.osgi.source" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp" version="2.6.0.v20210201-2003"/>
+ <unit id="org.apache.sshd.sftp.source" version="2.6.0.v20210201-2003"/>
<unit id="org.assertj" version="3.14.0.v20200120-1926"/>
<unit id="org.assertj.source" version="3.14.0.v20200120-1926"/>
<unit id="org.bouncycastle.bcpg" version="1.65.0.v20200527-1955"/>
@@ -84,7 +86,7 @@
<unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
<unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
<unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
- <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository"/>
+ <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd
index 06ccecbd5d..5aa63be64f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd
@@ -1,7 +1,7 @@
target "jgit-4.9" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/S20201118210000.tpd"
+include "orbit/S20210216215844.tpd"
location "https://download.eclipse.org/releases/2018-09/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20201118210000.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20201130205003-2020-12.tpd
index a00a5e7c87..08a0846de7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20201118210000.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20201130205003-2020-12.tpd
@@ -1,7 +1,7 @@
-target "S20201118210000" with source configurePhase
+target "R20201130205003-2020-12" with source configurePhase
// see https://download.eclipse.org/tools/orbit/downloads/
-location "https://download.eclipse.org/tools/orbit/downloads/drops/S20201118210000/repository" {
+location "https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/repository" {
com.google.gson [2.8.2.v20180104-1110,2.8.2.v20180104-1110]
com.google.gson.source [2.8.2.v20180104-1110,2.8.2.v20180104-1110]
com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20210216215844.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20210216215844.tpd
new file mode 100644
index 0000000000..29e5bc800b
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20210216215844.tpd
@@ -0,0 +1,66 @@
+target "S20210216215844" with source configurePhase
+// see https://download.eclipse.org/tools/orbit/downloads/
+
+location "https://download.eclipse.org/tools/orbit/downloads/drops/S20210216215844/repository" {
+ com.google.gson [2.8.6.v20201231-1626,2.8.6.v20201231-1626]
+ com.google.gson.source [2.8.6.v20201231-1626,2.8.6.v20201231-1626]
+ com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
+ com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
+ com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
+ com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
+ javaewah [1.1.7.v20200107-0831,1.1.7.v20200107-0831]
+ javaewah.source [1.1.7.v20200107-0831,1.1.7.v20200107-0831]
+ javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
+ javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800]
+ net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
+ net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
+ net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
+ net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
+ net.i2p.crypto.eddsa [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
+ net.i2p.crypto.eddsa.source [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
+ org.apache.ant [1.10.9.v20201106-1946,1.10.9.v20201106-1946]
+ org.apache.ant.source [1.10.9.v20201106-1946,1.10.9.v20201106-1946]
+ org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
+ org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
+ org.apache.commons.compress [1.19.0.v20200106-2343,1.19.0.v20200106-2343]
+ org.apache.commons.compress.source [1.19.0.v20200106-2343,1.19.0.v20200106-2343]
+ org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
+ org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
+ org.apache.httpcomponents.httpclient [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
+ org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
+ org.apache.httpcomponents.httpcore [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
+ org.apache.httpcomponents.httpcore.source [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
+ org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
+ org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
+ org.apache.sshd.osgi [2.6.0.v20210201-2003,2.6.0.v20210201-2003]
+ org.apache.sshd.osgi.source [2.6.0.v20210201-2003,2.6.0.v20210201-2003]
+ org.apache.sshd.sftp [2.6.0.v20210201-2003,2.6.0.v20210201-2003]
+ org.apache.sshd.sftp.source [2.6.0.v20210201-2003,2.6.0.v20210201-2003]
+ org.assertj [3.14.0.v20200120-1926,3.14.0.v20200120-1926]
+ org.assertj.source [3.14.0.v20200120-1926,3.14.0.v20200120-1926]
+ org.bouncycastle.bcpg [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
+ org.bouncycastle.bcpg.source [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
+ org.bouncycastle.bcpkix [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
+ org.bouncycastle.bcpkix.source [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
+ org.bouncycastle.bcprov [1.65.1.v20200529-1514,1.65.1.v20200529-1514]
+ org.bouncycastle.bcprov.source [1.65.1.v20200529-1514,1.65.1.v20200529-1514]
+ org.hamcrest [1.1.0.v20090501071000,1.1.0.v20090501071000]
+ org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
+ org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
+ org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
+ org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
+ org.junit [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
+ org.junit.source [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
+ org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
+ org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
+ org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
+ org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
+ org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
+ org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
+ org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
+ org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
+ org.slf4j.binding.log4j12 [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
+ org.slf4j.binding.log4j12.source [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
+ org.tukaani.xz [1.8.0.v20180207-1613,1.8.0.v20180207-1613]
+ org.tukaani.xz.source [1.8.0.v20180207-1613,1.8.0.v20180207-1613]
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd
index 70c426c188..4eec8aa354 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd
@@ -1,20 +1,22 @@
target "jetty-9.4.x" with source configurePhase
-location jetty-9.4.30 "https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/" {
- org.eclipse.jetty.client [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.client.source [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.continuation [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.continuation.source [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.http [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.http.source [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.io [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.io.source [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.security [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.security.source [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.server [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.server.source [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.servlet [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.servlet.source [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.util [9.4.30.v20200611,9.4.30.v20200611]
- org.eclipse.jetty.util.source [9.4.30.v20200611,9.4.30.v20200611]
+location jetty-9.4.36 "https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/" {
+ org.eclipse.jetty.client [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.client.source [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.continuation [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.continuation.source [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.http [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.http.source [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.io [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.io.source [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.security [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.security.source [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.server [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.server.source [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.servlet [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.servlet.source [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.util [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.util.source [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.util.ajax [9.4.36.v20210114,9.4.36.v20210114]
+ org.eclipse.jetty.util.ajax.source [9.4.36.v20210114,9.4.36.v20210114]
}
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 165854d21e..313c6632c2 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -165,7 +165,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
- <version>3.0.0-M1</version>
+ <version>3.0.0-M3</version>
<executions>
<execution>
<id>enforce-maven</id>
@@ -175,7 +175,7 @@
<configuration>
<rules>
<requireMavenVersion>
- <version>3.5.2</version>
+ <version>3.6.3</version>
</requireMavenVersion>
</rules>
</configuration>
@@ -294,12 +294,12 @@
<plugin>
<groupId>org.eclipse.cbi.maven.plugins</groupId>
<artifactId>eclipse-jarsigner-plugin</artifactId>
- <version>1.1.5</version>
+ <version>1.1.7</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
- <version>3.0.0</version>
+ <version>3.2.0</version>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
@@ -318,7 +318,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
- <version>3.8.2</version>
+ <version>3.9.1</version>
</plugin>
</plugins>
</pluginManagement>
diff --git a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
index 3dd5840397..b853c6a7ed 100644
--- a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
@@ -51,8 +51,8 @@ org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java
index 2f09b7f122..4cbd61c692 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java
@@ -11,7 +11,9 @@ package org.eclipse.jgit.pgm;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import java.io.File;
@@ -25,6 +27,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.RefSpec;
@@ -64,6 +67,45 @@ public class CloneTest extends CLIRepositoryTestCase {
assertEquals("expected 1 branch", 1, branches.size());
}
+ @Test
+ public void testCloneInitialBranch() throws Exception {
+ createInitialCommit();
+
+ File gitDir = db.getDirectory();
+ String sourceURI = gitDir.toURI().toString();
+ File target = createTempDirectory("target");
+ String cmd = "git clone --branch master " + sourceURI + " "
+ + shellQuote(target.getPath());
+ String[] result = execute(cmd);
+ assertArrayEquals(new String[] {
+ "Cloning into '" + target.getPath() + "'...", "", "" }, result);
+
+ Git git2 = Git.open(target);
+ List<Ref> branches = git2.branchList().call();
+ assertEquals("expected 1 branch", 1, branches.size());
+
+ Repository db2 = git2.getRepository();
+ ObjectId head = db2.resolve("HEAD");
+ assertNotNull(head);
+ assertNotEquals(ObjectId.zeroId(), head);
+ ObjectId master = db2.resolve("master");
+ assertEquals(head, master);
+ }
+
+ @Test
+ public void testCloneInitialBranchMissing() throws Exception {
+ createInitialCommit();
+
+ File gitDir = db.getDirectory();
+ String sourceURI = gitDir.toURI().toString();
+ File target = createTempDirectory("target");
+ String cmd = "git clone --branch foo " + sourceURI + " "
+ + shellQuote(target.getPath());
+ Die e = assertThrows(Die.class, () -> execute(cmd));
+ assertEquals("Remote branch 'foo' not found in upstream origin",
+ e.getMessage());
+ }
+
private RevCommit createInitialCommit() throws Exception {
JGitTestUtil.writeTrashFile(db, "hello.txt", "world");
git.add().addFilepattern("hello.txt").call();
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/InitTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/InitTest.java
index 84474e33cd..88789d3383 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/InitTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/InitTest.java
@@ -11,11 +11,14 @@
package org.eclipse.jgit.pgm;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
import java.io.File;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -54,4 +57,22 @@ public class InitTest extends CLIRepositoryTestCase {
assertArrayEquals(expecteds, result);
}
+ @Test
+ public void testInitDirectoryInitialBranch() throws Exception {
+ File workDirectory = tempFolder.getRoot();
+ File gitDirectory = new File(workDirectory, Constants.DOT_GIT);
+
+ String[] result = execute(
+ "git init -b main '" + workDirectory.getCanonicalPath() + "'");
+
+ String[] expecteds = new String[] {
+ "Initialized empty Git repository in "
+ + gitDirectory.getCanonicalPath(),
+ "" };
+ assertArrayEquals(expecteds, result);
+
+ try (Repository repo = new FileRepository(gitDirectory)) {
+ assertEquals("refs/heads/main", repo.getFullBranch());
+ }
+ }
}
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index 6822784f77..c56224e3a9 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -8,6 +8,7 @@ Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: javax.servlet;version="[3.1.0,4.0.0)",
+ org.apache.commons.logging;version="[1.2,2.0)",
org.eclipse.jetty.server;version="[9.4.5,10.0.0)",
org.eclipse.jetty.server.handler;version="[9.4.5,10.0.0)",
org.eclipse.jetty.servlet;version="[9.4.5,10.0.0)",
@@ -22,12 +23,10 @@ Import-Package: javax.servlet;version="[3.1.0,4.0.0)",
org.eclipse.jgit.dircache;version="[6.0.0,6.1.0)",
org.eclipse.jgit.errors;version="[6.0.0,6.1.0)",
org.eclipse.jgit.gitrepo;version="[6.0.0,6.1.0)",
- org.eclipse.jgit.internal.ketch;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.storage.file;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.storage.io;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.storage.pack;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.storage.reftable;version="[6.0.0,6.1.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[6.0.0,6.1.0)",
org.eclipse.jgit.lfs;version="[6.0.0,6.1.0)",
org.eclipse.jgit.lfs.server;version="[6.0.0,6.1.0)",
org.eclipse.jgit.lfs.server.fs;version="[6.0.0,6.1.0)",
diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
index 062b9643a3..e645255e96 100644
--- a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
+++ b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
@@ -47,7 +47,6 @@ org.eclipse.jgit.pgm.debug.MakeCacheTree
org.eclipse.jgit.pgm.debug.ReadDirCache
org.eclipse.jgit.pgm.debug.ReadReftable
org.eclipse.jgit.pgm.debug.RebuildCommitGraph
-org.eclipse.jgit.pgm.debug.RebuildRefTree
org.eclipse.jgit.pgm.debug.ShowCacheTree
org.eclipse.jgit.pgm.debug.ShowCommands
org.eclipse.jgit.pgm.debug.ShowDirCache
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index 6112a272e4..83846ee8e9 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -77,14 +77,15 @@ invalidHttpProxyOnlyHttpSupported=Invalid http_proxy: {0}: Only http supported.
invalidRecurseSubmodulesMode=Invalid recurse submodules mode: {0}
invalidUntrackedFilesMode=Invalid untracked files mode ''{0}''
jgitVersion=jgit version {0}
-lineFormat={0}
-listeningOn=Listening on {0}
lfsNoAccessKey=No accessKey in {0}
lfsNoSecretKey=No secretKey in {0}
lfsProtocolUrl=LFS protocol URL: {0}
lfsStoreDirectory=LFS objects stored in: {0}
lfsStoreUrl=LFS store URL: {0}
lfsUnknownStoreType="Unknown LFS store type: {0}"
+lineFormat={0}
+listeningOn=Listening on {0}
+logNoSignatureVerifier="No signature verifier available"
mergeConflict=CONFLICT(content): Merge conflict in {0}
mergeCheckoutConflict=error: Your local changes to the following files would be overwritten by merge:
mergeFailed=Automatic merge failed; fix conflicts and then commit the result
@@ -118,7 +119,6 @@ metaVar_file=FILE
metaVar_filepattern=filepattern
metaVar_gitDir=GIT_DIR
metaVar_hostName=HOSTNAME
-metaVar_ketchServerType=SERVERTYPE
metaVar_lfsStorage=STORAGE
metaVar_linesOfContext=lines
metaVar_message=message
@@ -143,6 +143,7 @@ metaVar_s3Region=REGION
metaVar_s3StorageClass=STORAGE-CLASS
metaVar_seconds=SECONDS
metaVar_service=SERVICE
+metaVar_tagLocalUser=<GPG key ID>
metaVar_treeish=tree-ish
metaVar_uriish=uri-ish
metaVar_url=URL
@@ -246,7 +247,6 @@ usage_DisplayTheVersionOfJgit=Display the version of jgit
usage_Gc=Cleanup unnecessary files and optimize the local repository
usage_Glog=View commit history as a graph
usage_IndexPack=Build pack index file for an existing packed archive
-usage_ketchServerType=Ketch server type
usage_LFSDirectory=Directory to store large objects
usage_LFSPort=Server http port
usage_LFSRunStore=Store (fs | s3), store lfs objects in file system or Amazon S3
@@ -266,8 +266,6 @@ usage_PreserveOldPacks=Preserve old pack files by moving them into the preserved
usage_PrunePreserved=Remove the preserved subdirectory containing previously preserved old pack files before repacking, and before preserving more old pack files
usage_ReadDirCache= Read the DirCache 100 times
usage_RebuildCommitGraph=Recreate a repository from another one's commit graph
-usage_RebuildRefTree=Copy references into a RefTree
-usage_RebuildRefTreeEnable=set extensions.refStorage = reftree
usage_Remote=Manage set of tracked repositories
usage_RepositoryToReadFrom=Repository to read from
usage_RepositoryToReceiveInto=Repository to receive into
@@ -414,6 +412,7 @@ usage_show=Display one commit
usage_showRefNamesMatchingCommits=Show ref names matching commits
usage_showPatch=display patch
usage_showNotes=Add this ref to the list of note branches from which notes are displayed
+usage_showSignature=Verify signatures of signed commits in the log
usage_showTimeInMilliseconds=Show mtime in milliseconds
usage_squash=Squash commits as if a real merge happened, but do not make a commit or move the HEAD.
usage_srcPrefix=show the source prefix instead of "a/"
@@ -421,13 +420,19 @@ usage_sshDriver=Selects the built-in ssh library to use, JSch or Apache MINA ssh
usage_symbolicVersionForTheProject=Symbolic version for the project
usage_tags=fetch all tags
usage_notags=do not fetch tags
+usage_tagAnnotated=create an annotated tag, unsigned unless -s or -u are given, or config tag.gpgSign is true
usage_tagDelete=delete tag
-usage_tagMessage=tag message
+usage_tagLocalUser=create a signed annotated tag using the specified GPG key ID
+usage_tagMessage=create an annotated tag with the given message, unsigned unless -s or -u are given, or config tag.gpgSign is true, or tar.forceSignAnnotated is true and -a is not given
+usage_tagSign=create a signed annotated tag
+usage_tagNoSign=suppress signing the tag
+usage_tagVerify=Verify the GPG signature
usage_untrackedFilesMode=show untracked files
usage_updateRef=reference to update
usage_updateRemoteRefsFromAnotherRepository=Update remote refs from another repository
usage_useNameInsteadOfOriginToTrackUpstream=use <name> instead of 'origin' to track upstream
usage_checkoutBranchAfterClone=check out named branch instead of remote's HEAD
+usage_initialBranch=initial branch of the newly created repository (default 'master', can be configured via config option init.defaultBranch)
usage_viewCommitHistory=View commit history
usage_orphan=Create a new orphan branch. The first commit made on this new branch will have no parents and it will be the root of a new history totally disconnected from other branches and commits.
usernameFor=Username for {0}:
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
index 8f80d6d70e..f28915d3fa 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
@@ -18,6 +18,7 @@ import java.util.Collection;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
+import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.TextProgressMonitor;
@@ -50,6 +51,9 @@ class Clone extends AbstractFetchCommand implements CloneCommand.Callback {
@Option(name = "--recurse-submodules", usage = "usage_recurseSubmodules")
private boolean cloneSubmodules;
+ @Option(name = "--timeout", metaVar = "metaVar_seconds", usage = "usage_abortConnectionIfNoActivity")
+ int timeout = -1;
+
@Argument(index = 0, required = true, metaVar = "metaVar_uriish")
private String sourceUri;
@@ -90,9 +94,8 @@ class Clone extends AbstractFetchCommand implements CloneCommand.Callback {
CloneCommand command = Git.cloneRepository();
command.setURI(sourceUri).setRemote(remoteName).setBare(isBare)
- .setMirror(isMirror)
- .setNoCheckout(noCheckout).setBranch(branch)
- .setCloneSubmodules(cloneSubmodules);
+ .setMirror(isMirror).setNoCheckout(noCheckout).setBranch(branch)
+ .setCloneSubmodules(cloneSubmodules).setTimeout(timeout);
command.setGitDir(gitdir == null ? null : new File(gitdir));
command.setDirectory(localNameF);
@@ -108,6 +111,8 @@ class Clone extends AbstractFetchCommand implements CloneCommand.Callback {
db = command.call().getRepository();
if (msgs && db.resolve(Constants.HEAD) == null)
outw.println(CLIText.get().clonedEmptyRepository);
+ } catch (TransportException e) {
+ throw die(e.getMessage(), e);
} catch (InvalidRemoteException e) {
throw die(MessageFormat.format(CLIText.get().doesNotExist,
sourceUri), e);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
index bf9102552c..f987f2c806 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
@@ -13,19 +13,12 @@ package org.eclipse.jgit.pgm;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
-import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.internal.ketch.KetchLeader;
-import org.eclipse.jgit.internal.ketch.KetchLeaderCache;
-import org.eclipse.jgit.internal.ketch.KetchPreReceive;
-import org.eclipse.jgit.internal.ketch.KetchSystem;
-import org.eclipse.jgit.internal.ketch.KetchText;
-import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.storage.file.FileBasedConfig;
@@ -33,10 +26,7 @@ import org.eclipse.jgit.storage.file.WindowCacheConfig;
import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.transport.DaemonClient;
import org.eclipse.jgit.transport.DaemonService;
-import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.resolver.FileResolver;
-import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.SystemReader;
import org.kohsuke.args4j.Argument;
@@ -71,13 +61,6 @@ class Daemon extends TextBuiltin {
@Option(name = "--export-all", usage = "usage_exportWithoutGitDaemonExportOk")
boolean exportAll;
- @Option(name = "--ketch", metaVar = "metaVar_ketchServerType", usage = "usage_ketchServerType")
- KetchServerType ketchServerType;
-
- enum KetchServerType {
- LEADER;
- }
-
@Argument(required = true, metaVar = "metaVar_directory", usage = "usage_directoriesToExport")
List<File> directory = new ArrayList<>();
@@ -102,9 +85,9 @@ class Daemon extends TextBuiltin {
}
cfg = new FileBasedConfig(configFile, FS.DETECTED);
}
- cfg.load();
- new WindowCacheConfig().fromConfig(cfg).install();
- packConfig.fromConfig(cfg);
+ cfg.load();
+ new WindowCacheConfig().fromConfig(cfg).install();
+ packConfig.fromConfig(cfg);
int threads = packConfig.getThreads();
if (threads <= 0)
@@ -137,9 +120,6 @@ class Daemon extends TextBuiltin {
service(d, n).setOverridable(true);
for (String n : forbidOverride)
service(d, n).setOverridable(false);
- if (ketchServerType == KetchServerType.LEADER) {
- startKetchLeader(d);
- }
d.start();
outw.println(MessageFormat.format(CLIText.get().listeningOn, d.getAddress()));
}
@@ -162,24 +142,4 @@ class Daemon extends TextBuiltin {
throw die(MessageFormat.format(CLIText.get().serviceNotSupported, n));
return svc;
}
-
- private void startKetchLeader(org.eclipse.jgit.transport.Daemon daemon) {
- KetchSystem system = new KetchSystem();
- final KetchLeaderCache leaders = new KetchLeaderCache(system);
- final ReceivePackFactory<DaemonClient> factory;
-
- factory = daemon.getReceivePackFactory();
- daemon.setReceivePackFactory((DaemonClient req, Repository repo) -> {
- ReceivePack rp = factory.create(req, repo);
- KetchLeader leader;
- try {
- leader = leaders.get(repo);
- } catch (URISyntaxException err) {
- throw new ServiceNotEnabledException(
- KetchText.get().invalidFollowerUri, err);
- }
- rp.setPreReceiveHook(new KetchPreReceive(leader));
- return rp;
- });
- }
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java
index 7f59ef43dc..7a0d96d419 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java
@@ -24,6 +24,7 @@ import org.eclipse.jgit.api.InitCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.util.StringUtils;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
@@ -32,6 +33,10 @@ class Init extends TextBuiltin {
@Option(name = "--bare", usage = "usage_CreateABareRepository")
private boolean bare;
+ @Option(name = "--initial-branch", aliases = { "-b" },
+ metaVar = "metaVar_branchName", usage = "usage_initialBranch")
+ private String branch;
+
@Argument(index = 0, metaVar = "metaVar_directory")
private String directory;
@@ -54,6 +59,9 @@ class Init extends TextBuiltin {
}
Repository repository;
try {
+ if (!StringUtils.isEmptyOrNull(branch)) {
+ command.setInitialBranch(branch);
+ }
repository = command.call().getRepository();
outw.println(MessageFormat.format(
CLIText.get().initializedEmptyGitRepositoryIn,
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
index 55efd23c6a..353b64b9be 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2010, Google Inc.
- * Copyright (C) 2006-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2006, 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, 2021, Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -31,12 +31,17 @@ import org.eclipse.jgit.diff.RenameDetector;
import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.pgm.internal.VerificationUtils;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.util.GitDateFormatter;
@@ -68,6 +73,9 @@ class Log extends RevWalkTextBuiltin {
additionalNoteRefs.add(notesRef);
}
+ @Option(name = "--show-signature", usage = "usage_showSignature")
+ private boolean showSignature;
+
@Option(name = "--date", usage = "usage_date")
void dateFormat(String date) {
if (date.toLowerCase(Locale.ROOT).equals(date))
@@ -147,6 +155,10 @@ class Log extends RevWalkTextBuiltin {
// END -- Options shared with Diff
+ private GpgSignatureVerifier verifier;
+
+ private GpgConfig config;
+
Log() {
dateFormatter = new GitDateFormatter(Format.DEFAULT);
}
@@ -161,6 +173,7 @@ class Log extends RevWalkTextBuiltin {
/** {@inheritDoc} */
@Override
protected void run() {
+ config = new GpgConfig(db.getConfig());
diffFmt.setRepository(db);
try {
diffFmt.setPathFilter(pathFilter);
@@ -197,6 +210,9 @@ class Log extends RevWalkTextBuiltin {
throw die(e.getMessage(), e);
} finally {
diffFmt.close();
+ if (verifier != null) {
+ verifier.clear();
+ }
}
}
@@ -229,6 +245,9 @@ class Log extends RevWalkTextBuiltin {
}
outw.println();
+ if (showSignature) {
+ showSignature(c);
+ }
final PersonIdent author = c.getAuthorIdent();
outw.println(MessageFormat.format(CLIText.get().authorInfo, author.getName(), author.getEmailAddress()));
outw.println(MessageFormat.format(CLIText.get().dateInfo,
@@ -252,6 +271,27 @@ class Log extends RevWalkTextBuiltin {
outw.flush();
}
+ private void showSignature(RevCommit c) throws IOException {
+ if (c.getRawGpgSignature() == null) {
+ return;
+ }
+ if (verifier == null) {
+ GpgSignatureVerifierFactory factory = GpgSignatureVerifierFactory
+ .getDefault();
+ if (factory == null) {
+ throw die(CLIText.get().logNoSignatureVerifier, null);
+ }
+ verifier = factory.getVerifier();
+ }
+ SignatureVerification verification = verifier.verifySignature(c,
+ config);
+ if (verification == null) {
+ return;
+ }
+ VerificationUtils.writeVerification(outw, verification,
+ verifier.getName(), c.getCommitterIdent());
+ }
+
/**
* @param c
* @return <code>true</code> if at least one note was printed,
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java
index 055b48a157..83446ccd53 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java
@@ -79,7 +79,7 @@ class LsRemote extends TextBuiltin {
private void show(Ref ref, String name)
throws IOException {
- outw.print("ref: ");
+ outw.print("ref: "); //$NON-NLS-1$
outw.print(ref.getName());
outw.print('\t');
outw.print(name);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
index 5f9551e529..3beab60a8b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
@@ -29,10 +29,15 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.RevisionSyntaxException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.pgm.internal.VerificationUtils;
import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
@@ -41,6 +46,7 @@ import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.eclipse.jgit.util.RawParseUtils;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
@@ -58,6 +64,9 @@ class Show extends TextBuiltin {
@Option(name = "--", metaVar = "metaVar_path", handler = PathTreeFilterHandler.class)
protected TreeFilter pathFilter = TreeFilter.ALL;
+ @Option(name = "--show-signature", usage = "usage_showSignature")
+ private boolean showSignature;
+
// BEGIN -- Options shared with Diff
@Option(name = "-p", usage = "usage_showPatch")
boolean showPatch;
@@ -219,13 +228,20 @@ class Show extends TextBuiltin {
}
outw.println();
- final String[] lines = tag.getFullMessage().split("\n"); //$NON-NLS-1$
- for (String s : lines) {
- outw.print(" "); //$NON-NLS-1$
- outw.print(s);
- outw.println();
+ String fullMessage = tag.getFullMessage();
+ if (!fullMessage.isEmpty()) {
+ String[] lines = tag.getFullMessage().split("\n"); //$NON-NLS-1$
+ for (String s : lines) {
+ outw.println(s);
+ }
+ }
+ byte[] rawSignature = tag.getRawGpgSignature();
+ if (rawSignature != null) {
+ String[] lines = RawParseUtils.decode(rawSignature).split("\n"); //$NON-NLS-1$
+ for (String s : lines) {
+ outw.println(s);
+ }
}
-
outw.println();
}
@@ -253,6 +269,10 @@ class Show extends TextBuiltin {
c.getId().copyTo(outbuffer, outw);
outw.println();
+ if (showSignature) {
+ showSignature(c);
+ }
+
final PersonIdent author = c.getAuthorIdent();
outw.println(MessageFormat.format(CLIText.get().authorInfo,
author.getName(), author.getEmailAddress()));
@@ -291,4 +311,28 @@ class Show extends TextBuiltin {
}
outw.println();
}
+
+ private void showSignature(RevCommit c) throws IOException {
+ if (c.getRawGpgSignature() == null) {
+ return;
+ }
+ GpgSignatureVerifierFactory factory = GpgSignatureVerifierFactory
+ .getDefault();
+ if (factory == null) {
+ throw die(CLIText.get().logNoSignatureVerifier, null);
+ }
+ GpgSignatureVerifier verifier = factory.getVerifier();
+ GpgConfig config = new GpgConfig(db.getConfig());
+ try {
+ SignatureVerification verification = verifier.verifySignature(c,
+ config);
+ if (verification == null) {
+ return;
+ }
+ VerificationUtils.writeVerification(outw, verification,
+ verifier.getName(), c.getCommitterIdent());
+ } finally {
+ verifier.clear();
+ }
+ }
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
index b408b78f3c..e2cd31d198 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
@@ -4,7 +4,7 @@
* Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg.lists@dewire.com>
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2021 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -22,26 +22,59 @@ import java.util.List;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListTagCommand;
import org.eclipse.jgit.api.TagCommand;
+import org.eclipse.jgit.api.VerificationResult;
+import org.eclipse.jgit.api.VerifySignatureCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.pgm.internal.VerificationUtils;
+import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
@Command(common = true, usage = "usage_CreateATag")
class Tag extends TextBuiltin {
- @Option(name = "-f", usage = "usage_forceReplacingAnExistingTag")
+
+ @Option(name = "--force", aliases = { "-f" }, forbids = { "--delete",
+ "--verify" }, usage = "usage_forceReplacingAnExistingTag")
private boolean force;
- @Option(name = "-d", usage = "usage_tagDelete")
+ @Option(name = "--delete", aliases = { "-d" }, forbids = {
+ "--verify" }, usage = "usage_tagDelete")
private boolean delete;
- @Option(name = "-m", metaVar = "metaVar_message", usage = "usage_tagMessage")
- private String message = ""; //$NON-NLS-1$
+ @Option(name = "--annotate", aliases = {
+ "-a" }, forbids = { "--delete",
+ "--verify" }, usage = "usage_tagAnnotated")
+ private boolean annotated;
+
+ @Option(name = "-m", forbids = { "--delete",
+ "--verify" }, metaVar = "metaVar_message", usage = "usage_tagMessage")
+ private String message;
+
+ @Option(name = "--sign", aliases = { "-s" }, forbids = {
+ "--no-sign", "--delete", "--verify" }, usage = "usage_tagSign")
+ private boolean sign;
+
+ @Option(name = "--no-sign", usage = "usage_tagNoSign", forbids = {
+ "--sign", "--delete", "--verify" })
+ private boolean noSign;
+
+ @Option(name = "--local-user", aliases = {
+ "-u" }, forbids = { "--delete",
+ "--verify" }, metaVar = "metaVar_tagLocalUser", usage = "usage_tagLocalUser")
+ private String gpgKeyId;
+
+ @Option(name = "--verify", aliases = { "-v" }, forbids = { "--delete",
+ "--force", "--annotate", "-m", "--sign", "--no-sign",
+ "--local-user" }, usage = "usage_tagVerify")
+ private boolean verify;
@Argument(index = 0, metaVar = "metaVar_name")
private String tagName;
@@ -54,7 +87,25 @@ class Tag extends TextBuiltin {
protected void run() {
try (Git git = new Git(db)) {
if (tagName != null) {
- if (delete) {
+ if (verify) {
+ VerifySignatureCommand verifySig = git.verifySignature()
+ .setMode(VerifySignatureCommand.VerifyMode.TAGS)
+ .addName(tagName);
+
+ VerificationResult verification = verifySig.call()
+ .get(tagName);
+ if (verification == null) {
+ showUnsigned(git, tagName);
+ } else {
+ Throwable error = verification.getException();
+ if (error != null) {
+ throw die(error.getMessage(), error);
+ }
+ writeVerification(verifySig.getVerifier().getName(),
+ (RevTag) verification.getObject(),
+ verification.getVerification());
+ }
+ } else if (delete) {
List<String> deletedTags = git.tagDelete().setTags(tagName)
.call();
if (deletedTags.isEmpty()) {
@@ -70,6 +121,18 @@ class Tag extends TextBuiltin {
command.setObjectId(walk.parseAny(object));
}
}
+ if (noSign) {
+ command.setSigned(false);
+ } else if (sign) {
+ command.setSigned(true);
+ }
+ if (annotated) {
+ command.setAnnotated(true);
+ } else if (message == null && !sign && gpgKeyId == null) {
+ // None of -a, -m, -s, -u given
+ command.setAnnotated(false);
+ }
+ command.setSigningKey(gpgKeyId);
try {
command.call();
} catch (RefAlreadyExistsException e) {
@@ -88,4 +151,36 @@ class Tag extends TextBuiltin {
throw die(e.getMessage(), e);
}
}
+
+ private void showUnsigned(Git git, String wantedTag) throws IOException {
+ ObjectId id = git.getRepository().resolve(wantedTag);
+ if (id != null && !ObjectId.zeroId().equals(id)) {
+ try (RevWalk walk = new RevWalk(git.getRepository())) {
+ showTag(walk.parseTag(id));
+ }
+ } else {
+ throw die(
+ MessageFormat.format(CLIText.get().tagNotFound, wantedTag));
+ }
+ }
+
+ private void showTag(RevTag tag) throws IOException {
+ outw.println("object " + tag.getObject().name()); //$NON-NLS-1$
+ outw.println("type " + Constants.typeString(tag.getObject().getType())); //$NON-NLS-1$
+ outw.println("tag " + tag.getTagName()); //$NON-NLS-1$
+ outw.println("tagger " + tag.getTaggerIdent().toExternalString()); //$NON-NLS-1$
+ outw.println();
+ outw.print(tag.getFullMessage());
+ }
+
+ private void writeVerification(String name, RevTag tag,
+ SignatureVerification verification) throws IOException {
+ showTag(tag);
+ if (verification == null) {
+ outw.println();
+ return;
+ }
+ VerificationUtils.writeVerification(outw, verification, name,
+ tag.getTaggerIdent());
+ }
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java
index 0b02dd148d..f70e72d434 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java
@@ -63,7 +63,7 @@ public abstract class TextBuiltin {
private boolean help;
@Option(name = "--ssh", usage = "usage_sshDriver")
- private SshDriver sshDriver = SshDriver.JSCH;
+ private SshDriver sshDriver = SshDriver.APACHE;
/**
* Input stream, typically this is standard input.
@@ -220,7 +220,7 @@ public abstract class TextBuiltin {
SshdSessionFactory factory = new SshdSessionFactory(
new JGitKeyCache(), new DefaultProxyDataFactory());
Runtime.getRuntime()
- .addShutdownHook(new Thread(() -> factory.close()));
+ .addShutdownHook(new Thread(factory::close));
SshSessionFactory.setInstance(factory);
break;
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/BenchmarkReftable.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/BenchmarkReftable.java
index 630fac549e..f23f4cf0ea 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/BenchmarkReftable.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/BenchmarkReftable.java
@@ -23,7 +23,9 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
import org.eclipse.jgit.internal.storage.file.FileReftableStack;
import org.eclipse.jgit.internal.storage.io.BlockSource;
@@ -47,6 +49,7 @@ class BenchmarkReftable extends TextBuiltin {
SEEK_COLD, SEEK_HOT,
BY_ID_COLD, BY_ID_HOT,
WRITE_STACK,
+ GET_REFS_EXCLUDING_REF
}
@Option(name = "--tries")
@@ -91,7 +94,11 @@ class BenchmarkReftable extends TextBuiltin {
case WRITE_STACK:
writeStack();
break;
- }
+ case GET_REFS_EXCLUDING_REF :
+ getRefsExcludingWithSeekPast(ref);
+ getRefsExcludingWithFilter(ref);
+ break;
+ }
}
private void printf(String fmt, Object... args) throws IOException {
@@ -315,4 +322,49 @@ class BenchmarkReftable extends TextBuiltin {
printf("%12s %10d usec %9.1f usec/run %5d runs", "reftable",
tot / 1000, (((double) tot) / tries) / 1000, tries);
}
+
+ @SuppressWarnings({"nls", "boxing"})
+ private void getRefsExcludingWithFilter(String prefix) throws Exception {
+ long startTime = System.nanoTime();
+ List<Ref> allRefs = new ArrayList<>();
+ try (FileInputStream in = new FileInputStream(reftablePath);
+ BlockSource src = BlockSource.from(in);
+ ReftableReader reader = new ReftableReader(src)) {
+ try (RefCursor rc = reader.allRefs()) {
+ while (rc.next()) {
+ allRefs.add(rc.getRef());
+ }
+ }
+ }
+ int total = allRefs.size();
+ allRefs = allRefs.stream().filter(r -> r.getName().startsWith(prefix)).collect(Collectors.toList());
+ int notStartWithPrefix = allRefs.size();
+ int startWithPrefix = total - notStartWithPrefix;
+ long totalTime = System.nanoTime() - startTime;
+ printf("total time the action took using filter: %10d usec", totalTime / 1000);
+ printf("number of refs that start with prefix: %d", startWithPrefix);
+ printf("number of refs that don't start with prefix: %d", notStartWithPrefix);
+ }
+
+ @SuppressWarnings({"nls", "boxing"})
+ private void getRefsExcludingWithSeekPast(String prefix) throws Exception {
+ long start = System.nanoTime();
+ try (FileInputStream in = new FileInputStream(reftablePath);
+ BlockSource src = BlockSource.from(in);
+ ReftableReader reader = new ReftableReader(src)) {
+ try (RefCursor rc = reader.allRefs()) {
+ while (rc.next()) {
+ if (rc.getRef().getName().startsWith(prefix)) {
+ break;
+ }
+ }
+ rc.seekPastPrefix(prefix);
+ while (rc.next()) {
+ rc.getRef();
+ }
+ }
+ }
+ long tot = System.nanoTime() - start;
+ printf("total time the action took using seek: %10d usec", tot / 1000);
+ }
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java
deleted file mode 100644
index 38951ba428..0000000000
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2015, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.pgm.debug;
-
-import static org.eclipse.jgit.lib.Constants.HEAD;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.jgit.internal.storage.reftree.RefTree;
-import org.eclipse.jgit.internal.storage.reftree.RefTreeDatabase;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.ConfigConstants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDatabase;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.StoredConfig;
-import org.eclipse.jgit.pgm.Command;
-import org.eclipse.jgit.pgm.TextBuiltin;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.kohsuke.args4j.Option;
-
-@Command(usage = "usage_RebuildRefTree")
-class RebuildRefTree extends TextBuiltin {
- @Option(name = "--enable", usage = "usage_RebuildRefTreeEnable")
- boolean enable;
-
- private String txnNamespace;
- private String txnCommitted;
-
- /** {@inheritDoc} */
- @Override
- protected void run() throws Exception {
- try (ObjectReader reader = db.newObjectReader();
- RevWalk rw = new RevWalk(reader);
- ObjectInserter inserter = db.newObjectInserter()) {
- RefDatabase refDb = db.getRefDatabase();
- if (refDb instanceof RefTreeDatabase) {
- RefTreeDatabase d = (RefTreeDatabase) refDb;
- refDb = d.getBootstrap();
- txnNamespace = d.getTxnNamespace();
- txnCommitted = d.getTxnCommitted();
- } else {
- RefTreeDatabase d = new RefTreeDatabase(db, refDb);
- txnNamespace = d.getTxnNamespace();
- txnCommitted = d.getTxnCommitted();
- }
-
- errw.format("Rebuilding %s from %s", //$NON-NLS-1$
- txnCommitted, refDb.getClass().getSimpleName());
- errw.println();
- errw.flush();
-
- CommitBuilder b = new CommitBuilder();
- Ref ref = refDb.exactRef(txnCommitted);
- RefUpdate update = refDb.newUpdate(txnCommitted, true);
- ObjectId oldTreeId;
-
- if (ref != null && ref.getObjectId() != null) {
- ObjectId oldId = ref.getObjectId();
- update.setExpectedOldObjectId(oldId);
- b.setParentId(oldId);
- oldTreeId = rw.parseCommit(oldId).getTree();
- } else {
- update.setExpectedOldObjectId(ObjectId.zeroId());
- oldTreeId = ObjectId.zeroId();
- }
-
- RefTree tree = rebuild(refDb);
- b.setTreeId(tree.writeTree(inserter));
- b.setAuthor(new PersonIdent(db));
- b.setCommitter(b.getAuthor());
- if (b.getTreeId().equals(oldTreeId)) {
- return;
- }
-
- update.setNewObjectId(inserter.insert(b));
- inserter.flush();
-
- RefUpdate.Result result = update.update(rw);
- switch (result) {
- case NEW:
- case FAST_FORWARD:
- break;
- default:
- throw die(String.format("%s: %s", update.getName(), result)); //$NON-NLS-1$
- }
-
- if (enable && !(db.getRefDatabase() instanceof RefTreeDatabase)) {
- StoredConfig cfg = db.getConfig();
- cfg.setInt(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 1);
- cfg.setString(ConfigConstants.CONFIG_EXTENSIONS_SECTION, null,
- ConfigConstants.CONFIG_KEY_REFSTORAGE,
- ConfigConstants.CONFIG_REFSTORAGE_REFTREE);
- cfg.save();
- errw.println("Enabled reftree."); //$NON-NLS-1$
- errw.flush();
- }
- }
- }
-
- private RefTree rebuild(RefDatabase refdb) throws IOException {
- RefTree tree = RefTree.newEmptyTree();
- List<org.eclipse.jgit.internal.storage.reftree.Command> cmds
- = new ArrayList<>();
-
- Ref head = refdb.exactRef(HEAD);
- if (head != null) {
- cmds.add(new org.eclipse.jgit.internal.storage.reftree.Command(
- null,
- head));
- }
-
- for (Ref r : refdb.getRefs()) {
- if (r.getName().equals(txnCommitted) || r.getName().equals(HEAD)
- || r.getName().startsWith(txnNamespace)) {
- continue;
- }
- cmds.add(new org.eclipse.jgit.internal.storage.reftree.Command(
- null,
- db.getRefDatabase().peel(r)));
- }
- tree.apply(cmds);
- return tree;
- }
-}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index c68019e5d0..991b3ba58a 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com>
- * Copyright (C) 2013, Obeo and others
+ * Copyright (C) 2013, 2021 Obeo and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -163,6 +163,7 @@ public class CLIText extends TranslationBundle {
/***/ public String lfsUnknownStoreType;
/***/ public String lineFormat;
/***/ public String listeningOn;
+ /***/ public String logNoSignatureVerifier;
/***/ public String mergeCheckoutConflict;
/***/ public String mergeConflict;
/***/ public String mergeFailed;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/VerificationUtils.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/VerificationUtils.java
new file mode 100644
index 0000000000..c1f8a86a8c
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/VerificationUtils.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.pgm.internal;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.util.GitDateFormatter;
+import org.eclipse.jgit.util.SignatureUtils;
+import org.eclipse.jgit.util.io.ThrowingPrintWriter;
+
+/**
+ * Utilities for signature verification.
+ */
+public final class VerificationUtils {
+
+ private VerificationUtils() {
+ // No instantiation
+ }
+
+ /**
+ * Writes information about a signature verification to the given writer.
+ *
+ * @param out
+ * to write to
+ * @param verification
+ * to show
+ * @param name
+ * of the verifier used
+ * @param creator
+ * of the object verified; used for time zone information
+ * @throws IOException
+ * if writing fails
+ */
+ public static void writeVerification(ThrowingPrintWriter out,
+ SignatureVerification verification, String name,
+ PersonIdent creator) throws IOException {
+ String[] text = SignatureUtils
+ .toString(verification, creator,
+ new GitDateFormatter(GitDateFormatter.Format.LOCALE))
+ .split("\n"); //$NON-NLS-1$
+ for (String line : text) {
+ out.print(name);
+ out.print(": "); //$NON-NLS-1$
+ out.println(line);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs
index 822846c4d0..cba893f04e 100644
--- a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs
@@ -51,8 +51,8 @@ org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
index fe3a2616f3..d881d87755 100644
--- a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
@@ -7,16 +7,18 @@ Bundle-Version: 6.0.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.apache.sshd.client.config.hosts;version="[2.4.0,2.5.0)",
- org.apache.sshd.common;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.auth;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.config.keys;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.keyprovider;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.session;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.net;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.security;version="[2.4.0,2.5.0)",
- org.apache.sshd.server;version="[2.4.0,2.5.0)",
- org.apache.sshd.server.forward;version="[2.4.0,2.5.0)",
+Import-Package: org.apache.sshd.client.config.hosts;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.auth;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.config.keys;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.helpers;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.keyprovider;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.session;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.net;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.security;version="[2.6.0,2.7.0)",
+ org.apache.sshd.core;version="[2.6.0,2.7.0)",
+ org.apache.sshd.server;version="[2.6.0,2.7.0)",
+ org.apache.sshd.server.forward;version="[2.6.0,2.7.0)",
org.eclipse.jgit.api;version="[6.0.0,6.1.0)",
org.eclipse.jgit.api.errors;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.transport.sshd.proxy;version="[6.0.0,6.1.0)",
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java
new file mode 100644
index 0000000000..0ad96b9acf
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.util.Arrays;
+
+import org.eclipse.jgit.junit.ssh.SshBasicTestBase;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.util.FS;
+
+public class ApacheSshProtocol2Test extends SshBasicTestBase {
+
+ @Override
+ protected SshSessionFactory createSessionFactory() {
+ SshdSessionFactory result = new SshdSessionFactory(new JGitKeyCache(),
+ null);
+ // The home directory is mocked at this point!
+ result.setHomeDirectory(FS.DETECTED.userHome());
+ result.setSshDirectory(sshDir);
+ return result;
+ }
+
+ @Override
+ protected void installConfig(String... config) {
+ File configFile = new File(sshDir, Constants.CONFIG);
+ if (config != null) {
+ try {
+ Files.write(configFile.toPath(), Arrays.asList(config));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ StoredConfig config = ((Repository) db).getConfig();
+ config.setInt("protocol", null, "version", 2);
+ config.save();
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
index 3427da667d..97f97f9028 100644
--- a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
@@ -9,6 +9,7 @@
*/
package org.eclipse.jgit.transport.sshd;
+import static org.apache.sshd.core.CoreModuleProperties.MAX_CONCURRENT_SESSIONS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -33,7 +34,6 @@ import java.util.stream.Collectors;
import org.apache.sshd.client.config.hosts.KnownHostEntry;
import org.apache.sshd.client.config.hosts.KnownHostHashValue;
-import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.PublicKeyEntry;
@@ -41,7 +41,6 @@ import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.util.net.SshdSocketAddress;
import org.apache.sshd.server.ServerAuthenticationManager;
-import org.apache.sshd.server.ServerFactoryManager;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.forward.StaticDecisionForwardingFilter;
import org.eclipse.jgit.api.Git;
@@ -216,8 +215,8 @@ public class ApacheSshTest extends SshTestBase {
*/
@Test
public void testCloneAndFetchWithSessionLimit() throws Exception {
- PropertyResolverUtils.updateProperty(server.getPropertyResolver(),
- ServerFactoryManager.MAX_CONCURRENT_SESSIONS, 2);
+ MAX_CONCURRENT_SESSIONS
+ .set(server.getPropertyResolver(), Integer.valueOf(2));
File localClone = cloneWith("ssh://localhost/doesntmatter",
defaultCloneDir, null, //
"Host localhost", //
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
index 96b40ad15e..faa52b26ac 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
@@ -33,49 +33,51 @@ Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.0.0";x-inter
org.apache.sshd.client.session,
org.apache.sshd.client.keyverifier"
Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
- org.apache.sshd.agent;version="[2.4.0,2.5.0)",
- org.apache.sshd.client;version="[2.4.0,2.5.0)",
- org.apache.sshd.client.auth;version="[2.4.0,2.5.0)",
- org.apache.sshd.client.auth.keyboard;version="[2.4.0,2.5.0)",
- org.apache.sshd.client.auth.password;version="[2.4.0,2.5.0)",
- org.apache.sshd.client.auth.pubkey;version="[2.4.0,2.5.0)",
- org.apache.sshd.client.channel;version="[2.4.0,2.5.0)",
- org.apache.sshd.client.config.hosts;version="[2.4.0,2.5.0)",
- org.apache.sshd.client.config.keys;version="[2.4.0,2.5.0)",
- org.apache.sshd.client.future;version="[2.4.0,2.5.0)",
- org.apache.sshd.client.keyverifier;version="[2.4.0,2.5.0)",
- org.apache.sshd.client.session;version="[2.4.0,2.5.0)",
- org.apache.sshd.client.session.forward;version="[2.4.0,2.5.0)",
- org.apache.sshd.client.subsystem.sftp;version="[2.4.0,2.5.0)",
- org.apache.sshd.common;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.auth;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.channel;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.compression;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.config.keys;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.config.keys.loader;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.config.keys.loader.openssh.kdf;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.digest;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.forward;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.future;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.helpers;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.io;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.kex;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.keyprovider;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.mac;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.random;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.session;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.session.helpers;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.signature;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.subsystem.sftp;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.buffer;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.closeable;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.io;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.io.resource;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.logging;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.net;version="[2.4.0,2.5.0)",
- org.apache.sshd.common.util.security;version="[2.4.0,2.5.0)",
- org.apache.sshd.server.auth;version="[2.4.0,2.5.0)",
+ org.apache.sshd.agent;version="[2.6.0,2.7.0)",
+ org.apache.sshd.client;version="[2.6.0,2.7.0)",
+ org.apache.sshd.client.auth;version="[2.6.0,2.7.0)",
+ org.apache.sshd.client.auth.keyboard;version="[2.6.0,2.7.0)",
+ org.apache.sshd.client.auth.password;version="[2.6.0,2.7.0)",
+ org.apache.sshd.client.auth.pubkey;version="[2.6.0,2.7.0)",
+ org.apache.sshd.client.channel;version="[2.6.0,2.7.0)",
+ org.apache.sshd.client.config.hosts;version="[2.6.0,2.7.0)",
+ org.apache.sshd.client.config.keys;version="[2.6.0,2.7.0)",
+ org.apache.sshd.client.future;version="[2.6.0,2.7.0)",
+ org.apache.sshd.client.keyverifier;version="[2.6.0,2.7.0)",
+ org.apache.sshd.client.session;version="[2.6.0,2.7.0)",
+ org.apache.sshd.client.session.forward;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.auth;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.channel;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.compression;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.config.keys;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.config.keys.loader;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.config.keys.loader.openssh.kdf;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.digest;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.forward;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.future;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.helpers;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.io;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.kex;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.keyprovider;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.mac;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.random;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.session;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.session.helpers;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.signature;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.buffer;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.closeable;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.io;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.io.resource;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.logging;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.net;version="[2.6.0,2.7.0)",
+ org.apache.sshd.common.util.security;version="[2.6.0,2.7.0)",
+ org.apache.sshd.core;version="[2.6.0,2.7.0)",
+ org.apache.sshd.server.auth;version="[2.6.0,2.7.0)",
+ org.apache.sshd.sftp;version="[2.6.0,2.7.0)",
+ org.apache.sshd.sftp.client;version="[2.6.0,2.7.0)",
+ org.apache.sshd.sftp.common;version="[2.6.0,2.7.0)",
org.eclipse.jgit.annotations;version="[6.0.0,6.1.0)",
org.eclipse.jgit.errors;version="[6.0.0,6.1.0)",
org.eclipse.jgit.fnmatch;version="[6.0.0,6.1.0)",
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
index 0d6f3027f2..66713ba632 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
@@ -10,6 +10,7 @@
package org.eclipse.jgit.internal.transport.sshd;
import static java.text.MessageFormat.format;
+import static org.apache.sshd.core.CoreModuleProperties.MAX_IDENTIFICATION_SIZE;
import java.io.IOException;
import java.io.StreamCorruptedException;
@@ -29,19 +30,14 @@ import java.util.Set;
import org.apache.sshd.client.ClientFactoryManager;
import org.apache.sshd.client.config.hosts.HostConfigEntry;
-import org.apache.sshd.client.future.AuthFuture;
import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
import org.apache.sshd.client.session.ClientSessionImpl;
-import org.apache.sshd.client.session.ClientUserAuthService;
import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.PropertyResolver;
-import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.SshException;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.io.IoSession;
import org.apache.sshd.common.io.IoWriteFuture;
-import org.apache.sshd.common.kex.KexState;
import org.apache.sshd.common.util.Readable;
import org.apache.sshd.common.util.buffer.Buffer;
import org.eclipse.jgit.errors.InvalidPatternException;
@@ -66,7 +62,8 @@ public class JGitClientSession extends ClientSessionImpl {
* protocol version exchange. 64kb is what OpenSSH < 8.0 read; OpenSSH 8.0
* changed it to 8Mb, but that seems excessive for the purpose stated in RFC
* 4253. The Apache MINA sshd default in
- * {@link FactoryManager#DEFAULT_MAX_IDENTIFICATION_SIZE} is 16kb.
+ * {@link org.apache.sshd.core.CoreModuleProperties#MAX_IDENTIFICATION_SIZE}
+ * is 16kb.
*/
private static final int DEFAULT_MAX_IDENTIFICATION_SIZE = 64 * 1024;
@@ -77,17 +74,6 @@ public class JGitClientSession extends ClientSessionImpl {
private volatile StatefulProxyConnector proxyHandler;
/**
- * Work-around for bug 565394 / SSHD-1050; remove when using sshd 2.6.0.
- */
- private volatile AuthFuture authFuture;
-
- /** Records exceptions before there is an authFuture. */
- private List<Throwable> earlyErrors = new ArrayList<>();
-
- /** Guards setting an earlyError and the authFuture together. */
- private final Object errorLock = new Object();
-
- /**
* @param manager
* @param session
* @throws Exception
@@ -97,125 +83,6 @@ public class JGitClientSession extends ClientSessionImpl {
super(manager, session);
}
- // BEGIN Work-around for bug 565394 / SSHD-1050
- // Remove when using sshd 2.6.0.
-
- @Override
- public AuthFuture auth() throws IOException {
- if (getUsername() == null) {
- throw new IllegalStateException(
- SshdText.get().sessionWithoutUsername);
- }
- ClientUserAuthService authService = getUserAuthService();
- String serviceName = nextServiceName();
- List<Throwable> errors = null;
- AuthFuture future;
- // Guard both getting early errors and setting authFuture
- synchronized (errorLock) {
- future = authService.auth(serviceName);
- if (future == null) {
- // Internal error; no translation.
- throw new IllegalStateException(
- "No auth future generated by service '" //$NON-NLS-1$
- + serviceName + '\'');
- }
- errors = earlyErrors;
- earlyErrors = null;
- authFuture = future;
- }
- if (errors != null && !errors.isEmpty()) {
- Iterator<Throwable> iter = errors.iterator();
- Throwable first = iter.next();
- iter.forEachRemaining(t -> {
- if (t != first && t != null) {
- first.addSuppressed(t);
- }
- });
- // Mark the future as having had an exception; just to be on the
- // safe side. Actually, there shouldn't be anyone waiting on this
- // future yet.
- future.setException(first);
- if (log.isDebugEnabled()) {
- log.debug("auth({}) early exception type={}: {}", //$NON-NLS-1$
- this, first.getClass().getSimpleName(),
- first.getMessage());
- }
- if (first instanceof SshException) {
- throw new SshException(
- ((SshException) first).getDisconnectCode(),
- first.getMessage(), first);
- }
- throw new IOException(first.getMessage(), first);
- }
- return future;
- }
-
- @Override
- protected void signalAuthFailure(AuthFuture future, Throwable t) {
- signalAuthFailure(t);
- }
-
- private void signalAuthFailure(Throwable t) {
- AuthFuture future = authFuture;
- if (future == null) {
- synchronized (errorLock) {
- if (earlyErrors != null) {
- earlyErrors.add(t);
- }
- future = authFuture;
- }
- }
- if (future != null) {
- future.setException(t);
- }
- if (log.isDebugEnabled()) {
- boolean signalled = future != null && t == future.getException();
- log.debug("signalAuthFailure({}) type={}, signalled={}: {}", this, //$NON-NLS-1$
- t.getClass().getSimpleName(), Boolean.valueOf(signalled),
- t.getMessage());
- }
- }
-
- @Override
- public void exceptionCaught(Throwable t) {
- signalAuthFailure(t);
- super.exceptionCaught(t);
- }
-
- @Override
- protected void preClose() {
- signalAuthFailure(
- new SshException(SshdText.get().authenticationOnClosedSession));
- super.preClose();
- }
-
- @Override
- protected void handleDisconnect(int code, String msg, String lang,
- Buffer buffer) throws Exception {
- signalAuthFailure(new SshException(code, msg));
- super.handleDisconnect(code, msg, lang, buffer);
- }
-
- @Override
- protected <C extends Collection<ClientSessionEvent>> C updateCurrentSessionState(
- C newState) {
- if (closeFuture.isClosed()) {
- newState.add(ClientSessionEvent.CLOSED);
- }
- if (isAuthenticated()) { // authFuture.isSuccess()
- newState.add(ClientSessionEvent.AUTHED);
- }
- if (KexState.DONE.equals(getKexState())) {
- AuthFuture future = authFuture;
- if (future == null || future.isFailure()) {
- newState.add(ClientSessionEvent.WAIT_AUTH);
- }
- }
- return newState;
- }
-
- // END Work-around for bug 565394 / SSHD-1050
-
/**
* Retrieves the {@link HostConfigEntry} this session was created for.
*
@@ -332,22 +199,6 @@ public class JGitClientSession extends ClientSessionImpl {
}
@Override
- protected void checkKeys() throws SshException {
- ServerKeyVerifier serverKeyVerifier = getServerKeyVerifier();
- // The super implementation always uses
- // getIoSession().getRemoteAddress(). In case of a proxy connection,
- // that would be the address of the proxy!
- SocketAddress remoteAddress = getConnectAddress();
- PublicKey serverKey = getKex().getServerKey();
- if (!serverKeyVerifier.verifyServerKey(this, remoteAddress,
- serverKey)) {
- throw new SshException(
- org.apache.sshd.common.SshConstants.SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE,
- SshdText.get().kexServerKeyInvalid);
- }
- }
-
- @Override
protected String resolveAvailableSignaturesProposal(
FactoryManager manager) {
Set<String> defaultSignatures = new LinkedHashSet<>();
@@ -477,9 +328,15 @@ public class JGitClientSession extends ClientSessionImpl {
throw new IllegalStateException(
"doReadIdentification of client called with server=true"); //$NON-NLS-1$
}
- int maxIdentSize = PropertyResolverUtils.getIntProperty(this,
- FactoryManager.MAX_IDENTIFICATION_SIZE,
- DEFAULT_MAX_IDENTIFICATION_SIZE);
+ Integer maxIdentLength = MAX_IDENTIFICATION_SIZE.get(this).orElse(null);
+ int maxIdentSize;
+ if (maxIdentLength == null || maxIdentLength
+ .intValue() < DEFAULT_MAX_IDENTIFICATION_SIZE) {
+ maxIdentSize = DEFAULT_MAX_IDENTIFICATION_SIZE;
+ MAX_IDENTIFICATION_SIZE.set(this, Integer.valueOf(maxIdentSize));
+ } else {
+ maxIdentSize = maxIdentLength.intValue();
+ }
int current = buffer.rpos();
int end = current + buffer.available();
if (current >= end) {
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java
index 4abd6e901a..ff8caaacc0 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java
@@ -9,7 +9,8 @@
*/
package org.eclipse.jgit.internal.transport.sshd;
-import org.apache.sshd.client.ClientAuthenticationManager;
+import static org.apache.sshd.core.CoreModuleProperties.PASSWORD_PROMPTS;
+
import org.apache.sshd.client.auth.keyboard.UserInteraction;
import org.apache.sshd.client.auth.password.UserAuthPassword;
import org.apache.sshd.client.session.ClientSession;
@@ -29,9 +30,7 @@ public class JGitPasswordAuthentication extends UserAuthPassword {
public void init(ClientSession session, String service) throws Exception {
super.init(session, service);
maxAttempts = Math.max(1,
- session.getIntProperty(
- ClientAuthenticationManager.PASSWORD_PROMPTS,
- ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS));
+ PASSWORD_PROMPTS.getRequired(session).intValue());
attempts = 0;
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
index beaaecaac9..74455dc808 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
@@ -10,6 +10,8 @@
package org.eclipse.jgit.internal.transport.sshd;
import static java.text.MessageFormat.format;
+import static org.apache.sshd.core.CoreModuleProperties.PASSWORD_PROMPTS;
+import static org.apache.sshd.core.CoreModuleProperties.PREFERRED_AUTHS;
import static org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.positive;
import java.io.IOException;
@@ -32,7 +34,6 @@ import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.stream.Collectors;
-import org.apache.sshd.client.ClientAuthenticationManager;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.config.hosts.HostConfigEntry;
import org.apache.sshd.client.future.ConnectFuture;
@@ -169,12 +170,15 @@ public class JGitSshClient extends SshClient {
Map<AttributeKey<?>, Object> data = new HashMap<>();
data.put(HOST_CONFIG_ENTRY, hostConfig);
data.put(ORIGINAL_REMOTE_ADDRESS, originalAddress);
+ data.put(TARGET_SERVER, new SshdSocketAddress(originalAddress));
String preferredAuths = hostConfig.getProperty(
SshConstants.PREFERRED_AUTHENTICATIONS,
resolveAttribute(PREFERRED_AUTHENTICATIONS));
if (!StringUtils.isEmptyOrNull(preferredAuths)) {
data.put(SessionAttributes.PROPERTIES,
- Collections.singletonMap(PREFERRED_AUTHS, preferredAuths));
+ Collections.singletonMap(
+ PREFERRED_AUTHS.getName(),
+ preferredAuths));
}
return new SessionAttributes(
AttributeRepository.ofAttributesMap(data),
@@ -267,8 +271,7 @@ public class JGitSshClient extends SshClient {
session.setCredentialsProvider(getCredentialsProvider());
}
int numberOfPasswordPrompts = getNumberOfPasswordPrompts(hostConfig);
- session.getProperties().put(PASSWORD_PROMPTS,
- Integer.valueOf(numberOfPasswordPrompts));
+ PASSWORD_PROMPTS.set(session, Integer.valueOf(numberOfPasswordPrompts));
List<Path> identities = hostConfig.getIdentities().stream()
.map(s -> {
try {
@@ -311,7 +314,7 @@ public class JGitSshClient extends SshClient {
log.warn(format(SshdText.get().configInvalidPositive,
SshConstants.NUMBER_OF_PASSWORD_PROMPTS, prompts));
}
- return ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS;
+ return PASSWORD_PROMPTS.getRequiredDefault().intValue();
}
/**
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java
index 97e0fcc7d2..6b0d9fb70b 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java
@@ -46,7 +46,7 @@ public class JGitSshConfig implements HostConfigEntryResolver {
@Override
public HostConfigEntry resolveEffectiveHost(String host, int port,
- SocketAddress localAddress, String username,
+ SocketAddress localAddress, String username, String proxyJump,
AttributeRepository attributes) throws IOException {
SshConfigStore.HostConfig entry = configFile == null
? SshConfigStore.EMPTY_CONFIG
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
index 5bc1115f62..47e09b75d7 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
@@ -346,11 +346,14 @@ public class OpenSshServerKeyDatabase
throws IOException {
KnownHostEntry hostEntry = entry.getHostEntry();
String oldLine = hostEntry.getConfigLine();
+ if (oldLine == null) {
+ return;
+ }
String newLine = updateHostKeyLine(oldLine, serverKey);
if (newLine == null || newLine.isEmpty()) {
return;
}
- if (oldLine == null || oldLine.isEmpty() || newLine.equals(oldLine)) {
+ if (oldLine.isEmpty() || newLine.equals(oldLine)) {
// Shouldn't happen.
return;
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java
index 078e411f29..2cd0669842 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java
@@ -9,6 +9,8 @@
*/
package org.eclipse.jgit.internal.transport.sshd;
+import static org.apache.sshd.core.CoreModuleProperties.PASSWORD_PROMPTS;
+
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
@@ -18,7 +20,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
-import org.apache.sshd.client.ClientAuthenticationManager;
import org.apache.sshd.common.AttributeRepository.AttributeKey;
import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
@@ -62,15 +63,8 @@ public class PasswordProviderWrapper implements FilePasswordProvider {
if (state == null) {
state = new PerSessionState();
state.delegate = factory.get();
- Integer maxNumberOfAttempts = context
- .getInteger(ClientAuthenticationManager.PASSWORD_PROMPTS);
- if (maxNumberOfAttempts != null
- && maxNumberOfAttempts.intValue() > 0) {
- state.delegate.setAttempts(maxNumberOfAttempts.intValue());
- } else {
- state.delegate.setAttempts(
- ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS);
- }
+ state.delegate.setAttempts(
+ PASSWORD_PROMPTS.getRequiredDefault().intValue());
context.setAttribute(STATE, state);
}
return state;
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java
index 8ac752bcce..e5d1e80f74 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java
@@ -135,7 +135,7 @@ public class HttpClientConnector extends AbstractClientProxyConnector {
byte[] data = eol(msg).toString().getBytes(US_ASCII);
Buffer buffer = new ByteArrayBuffer(data.length, false);
buffer.putRawBytes(data);
- session.writePacket(buffer).verify(getTimeout());
+ session.writeBuffer(buffer).verify(getTimeout());
}
private StringBuilder connect() {
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
index 78b8d456b4..8844efa6b7 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
@@ -235,7 +235,7 @@ public class Socks5ClientConnector extends AbstractClientProxyConnector {
buffer.putByte((byte) authenticationProposals.length);
buffer.putRawBytes(authenticationProposals);
state = ProtocolState.INIT;
- session.writePacket(buffer).verify(getTimeout());
+ session.writeBuffer(buffer).verify(getTimeout());
}
private byte[] getAuthenticationProposals() {
@@ -298,7 +298,7 @@ public class Socks5ClientConnector extends AbstractClientProxyConnector {
buffer.putByte((byte) ((port >> 8) & 0xFF));
buffer.putByte((byte) (port & 0xFF));
state = ProtocolState.CONNECTING;
- session.writePacket(buffer).verify(getTimeout());
+ session.writeBuffer(buffer).verify(getTimeout());
}
private void doPasswordAuth(IoSession session) throws Exception {
@@ -335,7 +335,7 @@ public class Socks5ClientConnector extends AbstractClientProxyConnector {
"No data for proxy authentication with " //$NON-NLS-1$
+ proxyAddress);
}
- session.writePacket(buffer).verify(getTimeout());
+ session.writeBuffer(buffer).verify(getTimeout());
} finally {
if (buffer != null) {
buffer.clear(true);
@@ -350,7 +350,7 @@ public class Socks5ClientConnector extends AbstractClientProxyConnector {
authenticator.process();
buffer = authenticator.getToken();
if (buffer != null) {
- session.writePacket(buffer).verify(getTimeout());
+ session.writeBuffer(buffer).verify(getTimeout());
}
} finally {
if (buffer != null) {
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
index 0fb0610b99..33b234b1f1 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
@@ -11,6 +11,7 @@ package org.eclipse.jgit.transport.sshd;
import static java.text.MessageFormat.format;
import static org.apache.sshd.common.SshConstants.SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE;
+import static org.apache.sshd.sftp.SftpModuleProperties.SFTP_CHANNEL_OPEN_TIMEOUT;
import java.io.Closeable;
import java.io.IOException;
@@ -24,6 +25,7 @@ import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@@ -37,23 +39,23 @@ import org.apache.sshd.client.config.hosts.HostConfigEntry;
import org.apache.sshd.client.future.ConnectFuture;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.client.session.forward.PortForwardingTracker;
-import org.apache.sshd.client.subsystem.sftp.SftpClient;
-import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle;
-import org.apache.sshd.client.subsystem.sftp.SftpClient.CopyMode;
-import org.apache.sshd.client.subsystem.sftp.SftpClientFactory;
import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.SshException;
import org.apache.sshd.common.future.CloseFuture;
import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.subsystem.sftp.SftpException;
import org.apache.sshd.common.util.io.IoUtils;
import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.apache.sshd.sftp.client.SftpClient;
+import org.apache.sshd.sftp.client.SftpClient.CloseableHandle;
+import org.apache.sshd.sftp.client.SftpClient.CopyMode;
+import org.apache.sshd.sftp.client.SftpClientFactory;
+import org.apache.sshd.sftp.common.SftpException;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.internal.transport.sshd.JGitSshClient;
import org.eclipse.jgit.internal.transport.sshd.SshdText;
import org.eclipse.jgit.transport.FtpChannel;
-import org.eclipse.jgit.transport.RemoteSession;
+import org.eclipse.jgit.transport.RemoteSession2;
import org.eclipse.jgit.transport.SshConstants;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.StringUtils;
@@ -61,11 +63,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * An implementation of {@link RemoteSession} based on Apache MINA sshd.
+ * An implementation of {@link org.eclipse.jgit.transport.RemoteSession
+ * RemoteSession} based on Apache MINA sshd.
*
* @since 5.2
*/
-public class SshdSession implements RemoteSession {
+public class SshdSession implements RemoteSession2 {
private static final Logger LOG = LoggerFactory
.getLogger(SshdSession.class);
@@ -203,7 +206,7 @@ public class SshdSession implements RemoteSession {
private HostConfigEntry getHostConfig(String username, String host,
int port) throws IOException {
HostConfigEntry entry = client.getHostConfigEntryResolver()
- .resolveEffectiveHost(host, port, null, username, null);
+ .resolveEffectiveHost(host, port, null, username, null, null);
if (entry == null) {
if (SshdSocketAddress.isIPv6Address(host)) {
return new HostConfigEntry("", host, port, username); //$NON-NLS-1$
@@ -290,8 +293,15 @@ public class SshdSession implements RemoteSession {
@Override
public Process exec(String commandName, int timeout) throws IOException {
+ return exec(commandName, Collections.emptyMap(), timeout);
+ }
+
+ @Override
+ public Process exec(String commandName, Map<String, String> environment,
+ int timeout) throws IOException {
@SuppressWarnings("resource")
- ChannelExec exec = session.createExecChannel(commandName);
+ ChannelExec exec = session.createExecChannel(commandName, null,
+ environment);
if (timeout <= 0) {
try {
exec.open().verify();
@@ -430,13 +440,12 @@ public class SshdSession implements RemoteSession {
@Override
public void connect(int timeout, TimeUnit unit) throws IOException {
if (timeout <= 0) {
- session.getProperties().put(
- SftpClient.SFTP_CHANNEL_OPEN_TIMEOUT,
- Long.valueOf(Long.MAX_VALUE));
+ // This timeout must not be null!
+ SFTP_CHANNEL_OPEN_TIMEOUT.set(session,
+ Duration.ofMillis(Long.MAX_VALUE));
} else {
- session.getProperties().put(
- SftpClient.SFTP_CHANNEL_OPEN_TIMEOUT,
- Long.valueOf(unit.toMillis(timeout)));
+ SFTP_CHANNEL_OPEN_TIMEOUT.set(session,
+ Duration.ofMillis(unit.toMillis(timeout)));
}
ftp = SftpClientFactory.instance().createSftpClient(session);
try {
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
index df0e1d28a4..357994d431 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
@@ -35,10 +35,13 @@ import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory;
import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.compression.BuiltinCompressions;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.config.keys.loader.openssh.kdf.BCryptKdfOptions;
import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.signature.Signature;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile;
@@ -205,6 +208,7 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable {
.hostConfigEntryResolver(configFile)
.serverKeyVerifier(new JGitServerKeyVerifier(
getServerKeyDatabase(home, sshDir)))
+ .signatureFactories(getSignatureFactories())
.compressionFactories(
new ArrayList<>(BuiltinCompressions.VALUES))
.build();
@@ -590,4 +594,35 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable {
protected String getDefaultPreferredAuthentications() {
return null;
}
+
+ /**
+ * Apache MINA sshd 2.6.0 has removed DSA, DSA_CERT and RSA_CERT. We have to
+ * set it up explicitly to still allow users to connect with DSA keys.
+ *
+ * @return a list of supported signature factories
+ */
+ @SuppressWarnings("deprecation")
+ private static List<NamedFactory<Signature>> getSignatureFactories() {
+ // @formatter:off
+ return Arrays.asList(
+ BuiltinSignatures.nistp256_cert,
+ BuiltinSignatures.nistp384_cert,
+ BuiltinSignatures.nistp521_cert,
+ BuiltinSignatures.ed25519_cert,
+ BuiltinSignatures.rsaSHA512_cert,
+ BuiltinSignatures.rsaSHA256_cert,
+ BuiltinSignatures.rsa_cert,
+ BuiltinSignatures.nistp256,
+ BuiltinSignatures.nistp384,
+ BuiltinSignatures.nistp521,
+ BuiltinSignatures.ed25519,
+ BuiltinSignatures.sk_ecdsa_sha2_nistp256,
+ BuiltinSignatures.sk_ssh_ed25519,
+ BuiltinSignatures.rsaSHA512,
+ BuiltinSignatures.rsaSHA256,
+ BuiltinSignatures.rsa,
+ BuiltinSignatures.dsa_cert,
+ BuiltinSignatures.dsa);
+ // @formatter:on
+ }
}
diff --git a/org.eclipse.jgit.ssh.jsch.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ssh.jsch.test/.settings/org.eclipse.jdt.core.prefs
index 2bc2cf30de..c16c986285 100644
--- a/org.eclipse.jgit.ssh.jsch.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ssh.jsch.test/.settings/org.eclipse.jdt.core.prefs
@@ -52,8 +52,8 @@ org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
index 9fb299a1cc..9ab1652a5e 100644
--- a/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
@@ -17,8 +17,4 @@ Import-Package: com.jcraft.jsch;version="[0.1.54,0.2.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.experimental.theories;version="[4.13,5.0.0)",
org.junit.runner;version="[4.13,5.0.0)"
-Export-Package: org.eclipse.jgit.transport;version="6.0.0";
- uses:="org.eclipse.jgit.transport,
- org.eclipse.jgit.junit,
- org.eclipse.jgit.junit.ssh"
Require-Bundle: org.hamcrest.core;bundle-version="[1.3.0,2.0.0)"
diff --git a/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/JSchSshProtocol2Test.java b/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/JSchSshProtocol2Test.java
new file mode 100644
index 0000000000..0929c55c07
--- /dev/null
+++ b/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/JSchSshProtocol2Test.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+//TODO(ms): move to org.eclipse.jgit.ssh.jsch in 6.0
+package org.eclipse.jgit.transport;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.util.Arrays;
+
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.junit.ssh.SshBasicTestBase;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.OpenSshConfig.Host;
+import org.eclipse.jgit.util.FS;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+public class JSchSshProtocol2Test extends SshBasicTestBase {
+
+ private class TestSshSessionFactory extends JschConfigSessionFactory {
+
+ @Override
+ protected void configure(Host hc, Session session) {
+ // Nothing
+ }
+
+ @Override
+ public synchronized RemoteSession getSession(URIish uri,
+ CredentialsProvider credentialsProvider, FS fs, int tms)
+ throws TransportException {
+ return super.getSession(uri, credentialsProvider, fs, tms);
+ }
+
+ @Override
+ protected JSch createDefaultJSch(FS fs) throws JSchException {
+ JSch defaultJSch = super.createDefaultJSch(fs);
+ if (knownHosts.exists()) {
+ defaultJSch.setKnownHosts(knownHosts.getAbsolutePath());
+ }
+ return defaultJSch;
+ }
+ }
+
+ @Override
+ protected SshSessionFactory createSessionFactory() {
+ return new TestSshSessionFactory();
+ }
+
+ @Override
+ protected void installConfig(String... config) {
+ SshSessionFactory factory = getSessionFactory();
+ assertTrue(factory instanceof JschConfigSessionFactory);
+ JschConfigSessionFactory j = (JschConfigSessionFactory) factory;
+ try {
+ j.setConfig(createConfig(config));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private OpenSshConfig createConfig(String... content) throws IOException {
+ File configFile = new File(sshDir, Constants.CONFIG);
+ if (content != null) {
+ Files.write(configFile.toPath(), Arrays.asList(content));
+ }
+ return new OpenSshConfig(getTemporaryDirectory(), configFile);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ StoredConfig config = ((Repository) db).getConfig();
+ config.setInt("protocol", null, "version", 2);
+ config.save();
+ }
+}
diff --git a/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/JschSession.java b/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/JschSession.java
index 858bdf3f7f..c7d0941b62 100644
--- a/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/JschSession.java
+++ b/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/JschSession.java
@@ -22,7 +22,9 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
@@ -44,7 +46,7 @@ import com.jcraft.jsch.SftpException;
* {@link org.eclipse.jgit.transport.JschConfigSessionFactory} is used to create
* the actual session passed to the constructor.
*/
-public class JschSession implements RemoteSession {
+public class JschSession implements RemoteSession2 {
final Session sock;
final URIish uri;
@@ -65,7 +67,14 @@ public class JschSession implements RemoteSession {
/** {@inheritDoc} */
@Override
public Process exec(String command, int timeout) throws IOException {
- return new JschProcess(command, timeout);
+ return exec(command, Collections.emptyMap(), timeout);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Process exec(String command, Map<String, String> environment,
+ int timeout) throws IOException {
+ return new JschProcess(command, environment, timeout);
}
/** {@inheritDoc} */
@@ -124,6 +133,8 @@ public class JschSession implements RemoteSession {
*
* @param commandName
* the command to execute
+ * @param environment
+ * environment variables to pass on
* @param tms
* the timeout value, in seconds, for the command.
* @throws TransportException
@@ -132,11 +143,17 @@ public class JschSession implements RemoteSession {
* @throws IOException
* on problems opening streams
*/
- JschProcess(String commandName, int tms)
- throws TransportException, IOException {
+ JschProcess(String commandName, Map<String, String> environment,
+ int tms) throws TransportException, IOException {
timeout = tms;
try {
channel = (ChannelExec) sock.openChannel("exec"); //$NON-NLS-1$
+ if (environment != null) {
+ for (Map.Entry<String, String> envVar : environment
+ .entrySet()) {
+ channel.setEnv(envVar.getKey(), envVar.getValue());
+ }
+ }
channel.setCommand(commandName);
setupStreams();
channel.connect(timeout > 0 ? timeout * 1000 : 0);
diff --git a/org.eclipse.jgit.test/.settings/edu.umd.cs.findbugs.core.prefs b/org.eclipse.jgit.test/.settings/edu.umd.cs.findbugs.core.prefs
new file mode 100644
index 0000000000..70c173f8dc
--- /dev/null
+++ b/org.eclipse.jgit.test/.settings/edu.umd.cs.findbugs.core.prefs
@@ -0,0 +1,145 @@
+#SpotBugs User Preferences
+#Fri Dec 04 11:35:48 CET 2020
+detectorExplicitSerialization=ExplicitSerialization|true
+detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
+detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
+detectorWrongMapIterator=WrongMapIterator|true
+detectorUnnecessaryMath=UnnecessaryMath|true
+detectorUselessSubclassMethod=UselessSubclassMethod|false
+filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,EXPERIMENTAL,I18N,MALICIOUS_CODE,MT_CORRECTNESS,PERFORMANCE,SECURITY,STYLE|false|15
+detectorURLProblems=URLProblems|true
+detectorIteratorIdioms=IteratorIdioms|true
+detectorMutableEnum=MutableEnum|true
+detectorFindNonShortCircuit=FindNonShortCircuit|true
+detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
+detectorVolatileUsage=VolatileUsage|true
+detectorFindNakedNotify=FindNakedNotify|true
+detectorFindUninitializedGet=FindUninitializedGet|true
+detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true
+detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
+detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
+detectorSwitchFallthrough=SwitchFallthrough|true
+detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
+detectorConfusedInheritance=ConfusedInheritance|true
+detectorSynchronizationOnSharedBuiltinConstant=SynchronizationOnSharedBuiltinConstant|true
+detectorMutableStaticFields=MutableStaticFields|true
+detectorInvalidJUnitTest=InvalidJUnitTest|true
+detectorInfiniteLoop=InfiniteLoop|true
+detectorFindRunInvocations=FindRunInvocations|true
+detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
+detectorXMLFactoryBypass=XMLFactoryBypass|true
+detectorFindOpenStream=FindOpenStream|true
+detectorCheckExpectedWarnings=CheckExpectedWarnings|false
+detectorHugeSharedStringConstants=HugeSharedStringConstants|true
+detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true
+detectorStringConcatenation=StringConcatenation|true
+detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
+detectorFinalizerNullsFields=FinalizerNullsFields|true
+detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
+detectorInefficientToArray=InefficientToArray|false
+detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
+detectorInconsistentAnnotations=InconsistentAnnotations|true
+detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
+detectorInstantiateStaticClass=InstantiateStaticClass|true
+detectorCheckRelaxingNullnessAnnotation=CheckRelaxingNullnessAnnotation|true
+detectorMethodReturnCheck=MethodReturnCheck|true
+detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
+detectorFindDoubleCheck=FindDoubleCheck|true
+detectorFindBadForLoop=FindBadForLoop|true
+detectorDefaultEncodingDetector=DefaultEncodingDetector|true
+detectorFindInconsistentSync2=FindInconsistentSync2|true
+detectorFindSpinLoop=FindSpinLoop|true
+detectorFindMaskedFields=FindMaskedFields|true
+detectorBooleanReturnNull=BooleanReturnNull|true
+detectorFindUnsyncGet=FindUnsyncGet|true
+detectorCrossSiteScripting=CrossSiteScripting|true
+detectorDroppedException=DroppedException|true
+detectorFindDeadLocalStores=FindDeadLocalStores|true
+detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
+detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
+detectorFindRefComparison=FindRefComparison|true
+detectorFindRoughConstants=FindRoughConstants|true
+detectorMutableLock=MutableLock|true
+detectorFindNullDeref=FindNullDeref|true
+detectorFindReturnRef=FindReturnRef|true
+detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
+detectorFindUselessControlFlow=FindUselessControlFlow|true
+detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
+detectorIDivResultCastToDouble=IDivResultCastToDouble|true
+detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true
+detectorFindSelfComparison=FindSelfComparison|true
+detectorFindFloatEquality=FindFloatEquality|true
+detectorFindComparatorProblems=FindComparatorProblems|true
+detectorRepeatedConditionals=RepeatedConditionals|true
+filter_settings_neg=NOISE|
+detectorInefficientMemberAccess=InefficientMemberAccess|false
+detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
+detectorNumberConstructor=NumberConstructor|true
+detectorDontAssertInstanceofInTests=DontAssertInstanceofInTests|true
+detectorFindFinalizeInvocations=FindFinalizeInvocations|true
+detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
+detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true
+detectorFindUnconditionalWait=FindUnconditionalWait|true
+detectorFindTwoLockWait=FindTwoLockWait|true
+detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
+detectorFindUnreleasedLock=FindUnreleasedLock|true
+detectorInefficientIndexOf=InefficientIndexOf|false
+detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
+detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
+detectorOverridingMethodsMustInvokeSuperDetector=OverridingMethodsMustInvokeSuperDetector|true
+detectorWaitInLoop=WaitInLoop|true
+detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true
+detectorBadUseOfReturnValue=BadUseOfReturnValue|true
+detectorFindSqlInjection=FindSqlInjection|true
+detectorUnreadFields=UnreadFields|true
+detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
+detectorFindUselessObjects=FindUselessObjects|true
+detectorBadAppletConstructor=BadAppletConstructor|false
+detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
+detectorSerializableIdiom=SerializableIdiom|true
+detectorNaming=Naming|true
+detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true
+detectorFormatStringChecker=FormatStringChecker|true
+detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
+detectorEmptyZipFileEntry=EmptyZipFileEntry|false
+detectorFindCircularDependencies=FindCircularDependencies|false
+detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
+detectorAtomicityProblem=AtomicityProblem|true
+detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
+detectorInitializationChain=InitializationChain|true
+detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true
+detectorOptionalReturnNull=OptionalReturnNull|true
+detectorStartInConstructor=StartInConstructor|true
+detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
+detectorRedundantConditions=RedundantConditions|true
+effort=default
+detectorRedundantInterfaces=RedundantInterfaces|true
+detectorDuplicateBranches=DuplicateBranches|true
+detectorCheckTypeQualifiers=CheckTypeQualifiers|true
+detectorComparatorIdiom=ComparatorIdiom|true
+detectorFindBadCast2=FindBadCast2|true
+detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
+excludefilter0=findBugs/FindBugsExcludeFilter.xml|true
+detectorBadResultSetAccess=BadResultSetAccess|true
+detectorIncompatMask=IncompatMask|true
+detectorCovariantArrayAssignment=CovariantArrayAssignment|false
+detectorDumbMethodInvocations=DumbMethodInvocations|true
+run_at_full_build=false
+detectorStaticCalendarDetector=StaticCalendarDetector|true
+detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
+detectorVarArgsProblems=VarArgsProblems|true
+detectorInefficientInitializationInsideLoop=InefficientInitializationInsideLoop|false
+detectorCloneIdiom=CloneIdiom|true
+detectorFindHEmismatch=FindHEmismatch|true
+detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
+detectorFindSelfComparison2=FindSelfComparison2|true
+detectorLazyInit=LazyInit|true
+detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
+detectorDontUseEnum=DontUseEnum|true
+detectorFindPuzzlers=FindPuzzlers|true
+detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false
+detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
+detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
+detector_threshold=2
+detectorPublicSemaphores=PublicSemaphores|false
+detectorDumbMethods=DumbMethods|true
diff --git a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
index 3dd5840397..b853c6a7ed 100644
--- a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
@@ -51,8 +51,8 @@ org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.test/BUILD b/org.eclipse.jgit.test/BUILD
index f12646e859..c9b5d37265 100644
--- a/org.eclipse.jgit.test/BUILD
+++ b/org.eclipse.jgit.test/BUILD
@@ -13,6 +13,8 @@ HELPERS = glob(
) + [PKG + c for c in [
"api/AbstractRemoteCommandTest.java",
"diff/AbstractDiffTestCase.java",
+ "internal/revwalk/ObjectReachabilityTestCase.java",
+ "internal/revwalk/ReachabilityCheckerTestCase.java",
"internal/storage/file/GcTestCase.java",
"internal/storage/file/PackIndexTestCase.java",
"internal/storage/file/XInputStream.java",
@@ -20,8 +22,6 @@ HELPERS = glob(
"nls/MissingPropertyBundle.java",
"nls/NoPropertiesBundle.java",
"nls/NonTranslatedBundle.java",
- "revwalk/ObjectReachabilityTestCase.java",
- "revwalk/ReachabilityCheckerTestCase.java",
"revwalk/RevQueueTestCase.java",
"revwalk/RevWalkTestCase.java",
"transport/ObjectIdMatcher.java",
@@ -44,8 +44,6 @@ EXCLUDED = [
PKG + "api/SecurityManagerMissingPermissionsTest.java",
]
-RESOURCES = glob(["resources/**"])
-
tests(tests = glob(
["tst/**/*.java"],
exclude = HELPERS + DATA + EXCLUDED,
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index d4cc7b00bd..ca6c587174 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -34,12 +34,12 @@ Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
org.eclipse.jgit.ignore.internal;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.fsck;version="[6.0.0,6.1.0)",
+ org.eclipse.jgit.internal.revwalk;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.storage.dfs;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.storage.file;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.storage.io;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.storage.pack;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.storage.reftable;version="[6.0.0,6.1.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.transport.connectivity;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.transport.http;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.transport.parser;version="[6.0.0,6.1.0)",
diff --git a/org.eclipse.jgit.test/findBugs/FindBugsExcludeFilter.xml b/org.eclipse.jgit.test/findBugs/FindBugsExcludeFilter.xml
new file mode 100644
index 0000000000..b4ef95379b
--- /dev/null
+++ b/org.eclipse.jgit.test/findBugs/FindBugsExcludeFilter.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<FindBugsFilter>
+ <!-- We want complete control over clone behavior and
+ don't want to use Object's clone implementation.
+ -->
+ <Match>
+ <Bug pattern="CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE" />
+ </Match>
+</FindBugsFilter>
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
index 0f98a63f5a..f2cceac4b3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
@@ -12,6 +12,7 @@ package org.eclipse.jgit.api;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import java.beans.Statement;
import java.io.BufferedInputStream;
@@ -28,6 +29,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
@@ -55,6 +57,7 @@ import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.StringUtils;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
public class ArchiveCommandTest extends RepositoryTestCase {
@@ -184,88 +187,63 @@ public class ArchiveCommandTest extends RepositoryTestCase {
@Test
public void archiveHeadAllFilesTarTimestamps() throws Exception {
- try (Git git = new Git(db)) {
- createTestContent(git);
- String fmt = "tar";
- File archive = new File(getTemporaryDirectory(),
- "archive." + format);
- archive(git, archive, fmt);
- ObjectId hash1 = ObjectId.fromRaw(IO.readFully(archive));
-
- try (InputStream fi = Files.newInputStream(archive.toPath());
- InputStream bi = new BufferedInputStream(fi);
- ArchiveInputStream o = new TarArchiveInputStream(bi)) {
- assertEntries(o);
- }
-
- Thread.sleep(WAIT);
- archive(git, archive, fmt);
- assertEquals(UNEXPECTED_DIFFERENT_HASH, hash1,
- ObjectId.fromRaw(IO.readFully(archive)));
- }
+ archiveHeadAllFiles("tar");
}
@Test
public void archiveHeadAllFilesTgzTimestamps() throws Exception {
- try (Git git = new Git(db)) {
- createTestContent(git);
- String fmt = "tgz";
- File archive = new File(getTemporaryDirectory(),
- "archive." + fmt);
- archive(git, archive, fmt);
- ObjectId hash1 = ObjectId.fromRaw(IO.readFully(archive));
+ archiveHeadAllFiles("tgz");
+ }
- try (InputStream fi = Files.newInputStream(archive.toPath());
- InputStream bi = new BufferedInputStream(fi);
- InputStream gzi = new GzipCompressorInputStream(bi);
- ArchiveInputStream o = new TarArchiveInputStream(gzi)) {
- assertEntries(o);
- }
+ @Test
+ public void archiveHeadAllFilesTbz2Timestamps() throws Exception {
+ archiveHeadAllFiles("tbz2");
+ }
- Thread.sleep(WAIT);
- archive(git, archive, fmt);
- assertEquals(UNEXPECTED_DIFFERENT_HASH, hash1,
- ObjectId.fromRaw(IO.readFully(archive)));
- }
+ @Test
+ public void archiveHeadAllFilesTxzTimestamps() throws Exception {
+ archiveHeadAllFiles("txz");
}
@Test
- public void archiveHeadAllFilesTbz2Timestamps() throws Exception {
- try (Git git = new Git(db)) {
- createTestContent(git);
- String fmt = "tbz2";
- File archive = new File(getTemporaryDirectory(),
- "archive." + fmt);
- archive(git, archive, fmt);
- ObjectId hash1 = ObjectId.fromRaw(IO.readFully(archive));
+ public void archiveHeadAllFilesZipTimestamps() throws Exception {
+ archiveHeadAllFiles("zip");
+ }
- try (InputStream fi = Files.newInputStream(archive.toPath());
- InputStream bi = new BufferedInputStream(fi);
- InputStream gzi = new BZip2CompressorInputStream(bi);
- ArchiveInputStream o = new TarArchiveInputStream(gzi)) {
- assertEntries(o);
- }
+ @Test
+ public void archiveHeadAllFilesTgzWithCompressionReducesArchiveSize() throws Exception {
+ archiveHeadAllFilesWithCompression("tgz");
+ }
- Thread.sleep(WAIT);
- archive(git, archive, fmt);
- assertEquals(UNEXPECTED_DIFFERENT_HASH, hash1,
- ObjectId.fromRaw(IO.readFully(archive)));
- }
+ @Test
+ public void archiveHeadAllFilesTbz2WithCompressionReducesArchiveSize() throws Exception {
+ archiveHeadAllFilesWithCompression("tbz2");
}
@Test
- public void archiveHeadAllFilesTxzTimestamps() throws Exception {
+ @Ignore
+ public void archiveHeadAllFilesTxzWithCompressionReducesArchiveSize() throws Exception {
+ // We ignore this test because the txz format consumes a lot of memory for high level
+ // compressions.
+ archiveHeadAllFilesWithCompression("txz");
+ }
+
+ @Test
+ public void archiveHeadAllFilesZipWithCompressionReducesArchiveSize() throws Exception {
+ archiveHeadAllFilesWithCompression("zip");
+ }
+
+ private void archiveHeadAllFiles(String fmt) throws Exception {
try (Git git = new Git(db)) {
createTestContent(git);
- String fmt = "txz";
- File archive = new File(getTemporaryDirectory(), "archive." + fmt);
+ File archive = new File(getTemporaryDirectory(),
+ "archive." + format);
archive(git, archive, fmt);
ObjectId hash1 = ObjectId.fromRaw(IO.readFully(archive));
try (InputStream fi = Files.newInputStream(archive.toPath());
InputStream bi = new BufferedInputStream(fi);
- InputStream gzi = new XZCompressorInputStream(bi);
- ArchiveInputStream o = new TarArchiveInputStream(gzi)) {
+ ArchiveInputStream o = createArchiveInputStream(fmt, bi)) {
assertEntries(o);
}
@@ -276,28 +254,44 @@ public class ArchiveCommandTest extends RepositoryTestCase {
}
}
- @Test
- public void archiveHeadAllFilesZipTimestamps() throws Exception {
+ @SuppressWarnings({ "serial", "boxing" })
+ private void archiveHeadAllFilesWithCompression(String fmt) throws Exception {
try (Git git = new Git(db)) {
- createTestContent(git);
- String fmt = "zip";
- File archive = new File(getTemporaryDirectory(), "archive." + fmt);
- archive(git, archive, fmt);
- ObjectId hash1 = ObjectId.fromRaw(IO.readFully(archive));
+ createLargeTestContent(git);
+ File archive = new File(getTemporaryDirectory(),
+ "archive." + format);
- try (InputStream fi = Files.newInputStream(archive.toPath());
- InputStream bi = new BufferedInputStream(fi);
- ArchiveInputStream o = new ZipArchiveInputStream(bi)) {
- assertEntries(o);
- }
+ archive(git, archive, fmt, new HashMap<String, Object>() {{
+ put("compression-level", 1);
+ }});
+ int sizeCompression1 = getNumBytes(archive);
- Thread.sleep(WAIT);
- archive(git, archive, fmt);
- assertEquals(UNEXPECTED_DIFFERENT_HASH, hash1,
- ObjectId.fromRaw(IO.readFully(archive)));
+ archive(git, archive, fmt, new HashMap<String, Object>() {{
+ put("compression-level", 9);
+ }});
+ int sizeCompression9 = getNumBytes(archive);
+
+ assertTrue(sizeCompression1 > sizeCompression9);
}
}
+ private static ArchiveInputStream createArchiveInputStream (String fmt, InputStream bi)
+ throws IOException {
+ switch (fmt) {
+ case "tar":
+ return new TarArchiveInputStream(bi);
+ case "tgz":
+ return new TarArchiveInputStream(new GzipCompressorInputStream(bi));
+ case "tbz2":
+ return new TarArchiveInputStream(new BZip2CompressorInputStream(bi));
+ case "txz":
+ return new TarArchiveInputStream(new XZCompressorInputStream(bi));
+ case "zip":
+ return new ZipArchiveInputStream(new BufferedInputStream(bi));
+ }
+ throw new IllegalArgumentException("Format " + fmt + " is not supported.");
+ }
+
private void createTestContent(Git git) throws IOException, GitAPIException,
NoFilepatternException, NoHeadException, NoMessageException,
UnmergedPathsException, ConcurrentRefUpdateException,
@@ -312,13 +306,40 @@ public class ArchiveCommandTest extends RepositoryTestCase {
git.commit().setMessage("updated file").call();
}
+ private void createLargeTestContent(Git git) throws IOException, GitAPIException,
+ NoFilepatternException, NoHeadException, NoMessageException,
+ UnmergedPathsException, ConcurrentRefUpdateException,
+ WrongRepositoryStateException, AbortedByHookException {
+ StringBuilder largeContent = new StringBuilder();
+ Random r = new Random();
+ for (int i = 0; i < 2000; i++) {
+ for (int j = 0; j < 80; j++) {
+ largeContent.append((char)(r.nextInt(26) + 'a'));
+ }
+ largeContent.append("\n");
+ }
+ writeTrashFile("large_file.txt", largeContent.toString());
+ git.add().addFilepattern("large_file.txt").call();
+ git.commit().setMessage("create file").call();
+ }
+
private static void archive(Git git, File archive, String fmt)
throws GitAPIException,
FileNotFoundException, AmbiguousObjectException,
IncorrectObjectTypeException, IOException {
+ archive(git, archive, fmt, new HashMap<>());
+ }
+
+ private static void archive(Git git, File archive, String fmt, Map<String,
+ Object> options)
+ throws GitAPIException,
+ FileNotFoundException, AmbiguousObjectException,
+ IncorrectObjectTypeException, IOException {
git.archive().setOutputStream(new FileOutputStream(archive))
.setFormat(fmt)
- .setTree(git.getRepository().resolve("HEAD")).call();
+ .setTree(git.getRepository().resolve("HEAD"))
+ .setFormatOptions(options)
+ .call();
}
private static void assertEntries(ArchiveInputStream o) throws IOException {
@@ -333,6 +354,13 @@ public class ArchiveCommandTest extends RepositoryTestCase {
assertEquals(UNEXPECTED_ARCHIVE_SIZE, 2, n);
}
+ private static int getNumBytes(File archive) throws Exception {
+ try (InputStream fi = Files.newInputStream(archive.toPath());
+ InputStream bi = new BufferedInputStream(fi)) {
+ return bi.available();
+ }
+ }
+
private static class MockFormat
implements ArchiveCommand.Format<MockOutputStream> {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
index 0a0a88c838..e520732513 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
@@ -147,6 +147,55 @@ public class CheckoutCommandTest extends RepositoryTestCase {
}
@Test
+ public void testCheckoutForcedNoChangeNotInIndex() throws Exception {
+ git.checkout().setCreateBranch(true).setName("test2").call();
+ File f = writeTrashFile("NewFile.txt", "New file");
+ git.add().addFilepattern("NewFile.txt").call();
+ git.commit().setMessage("New file created").call();
+ git.checkout().setName("test").call();
+ assertFalse("NewFile.txt should not exist", f.exists());
+ writeTrashFile("NewFile.txt", "New file");
+ git.add().addFilepattern("NewFile.txt").call();
+ git.commit().setMessage("New file created again with same content")
+ .call();
+ // Now remove the file from the index only. So it exists in both
+ // commits, and in the working tree, but not in the index.
+ git.rm().addFilepattern("NewFile.txt").setCached(true).call();
+ assertTrue("NewFile.txt should exist", f.isFile());
+ git.checkout().setForced(true).setName("test2").call();
+ assertTrue("NewFile.txt should exist", f.isFile());
+ assertEquals(Constants.R_HEADS + "test2", git.getRepository()
+ .exactRef(Constants.HEAD).getTarget().getName());
+ assertTrue("Force checkout should have undone git rm --cached",
+ git.status().call().isClean());
+ }
+
+ @Test
+ public void testCheckoutNoChangeNotInIndex() throws Exception {
+ git.checkout().setCreateBranch(true).setName("test2").call();
+ File f = writeTrashFile("NewFile.txt", "New file");
+ git.add().addFilepattern("NewFile.txt").call();
+ git.commit().setMessage("New file created").call();
+ git.checkout().setName("test").call();
+ assertFalse("NewFile.txt should not exist", f.exists());
+ writeTrashFile("NewFile.txt", "New file");
+ git.add().addFilepattern("NewFile.txt").call();
+ git.commit().setMessage("New file created again with same content")
+ .call();
+ // Now remove the file from the index only. So it exists in both
+ // commits, and in the working tree, but not in the index.
+ git.rm().addFilepattern("NewFile.txt").setCached(true).call();
+ assertTrue("NewFile.txt should exist", f.isFile());
+ git.checkout().setName("test2").call();
+ assertTrue("NewFile.txt should exist", f.isFile());
+ assertEquals(Constants.R_HEADS + "test2", git.getRepository()
+ .exactRef(Constants.HEAD).getTarget().getName());
+ org.eclipse.jgit.api.Status status = git.status().call();
+ assertEquals("[NewFile.txt]", status.getRemoved().toString());
+ assertEquals("[NewFile.txt]", status.getUntracked().toString());
+ }
+
+ @Test
public void testCreateBranchOnCheckout() throws Exception {
git.checkout().setCreateBranch(true).setName("test2").call();
assertNotNull(db.exactRef("refs/heads/test2"));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java
index 1c18b5a8b1..48d835ed26 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java
@@ -9,6 +9,7 @@
*/
package org.eclipse.jgit.api;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -21,8 +22,10 @@ import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.junit.MockSystemReader;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.util.SystemReader;
import org.junit.Before;
import org.junit.Test;
@@ -42,7 +45,73 @@ public class InitCommandTest extends RepositoryTestCase {
InitCommand command = new InitCommand();
command.setDirectory(directory);
try (Git git = command.call()) {
- assertNotNull(git.getRepository());
+ Repository r = git.getRepository();
+ assertNotNull(r);
+ assertEquals("refs/heads/master", r.getFullBranch());
+ }
+ }
+
+ @Test
+ public void testInitRepositoryMainInitialBranch()
+ throws IOException, JGitInternalException, GitAPIException {
+ File directory = createTempDirectory("testInitRepository");
+ InitCommand command = new InitCommand();
+ command.setDirectory(directory);
+ command.setInitialBranch("main");
+ try (Git git = command.call()) {
+ Repository r = git.getRepository();
+ assertNotNull(r);
+ assertEquals("refs/heads/main", r.getFullBranch());
+ }
+ }
+
+ @Test
+ public void testInitRepositoryCustomDefaultBranch()
+ throws Exception {
+ File directory = createTempDirectory("testInitRepository");
+ InitCommand command = new InitCommand();
+ command.setDirectory(directory);
+ MockSystemReader reader = (MockSystemReader) SystemReader.getInstance();
+ StoredConfig c = reader.getUserConfig();
+ String old = c.getString(ConfigConstants.CONFIG_INIT_SECTION, null,
+ ConfigConstants.CONFIG_KEY_DEFAULT_BRANCH);
+ c.setString(ConfigConstants.CONFIG_INIT_SECTION, null,
+ ConfigConstants.CONFIG_KEY_DEFAULT_BRANCH, "main");
+ try (Git git = command.call()) {
+ Repository r = git.getRepository();
+ assertNotNull(r);
+ assertEquals("refs/heads/main", r.getFullBranch());
+ } finally {
+ c.setString(ConfigConstants.CONFIG_INIT_SECTION, null,
+ ConfigConstants.CONFIG_KEY_DEFAULT_BRANCH, old);
+ }
+ }
+
+ @Test
+ public void testInitRepositoryNullInitialBranch() throws Exception {
+ File directory = createTempDirectory("testInitRepository");
+ InitCommand command = new InitCommand();
+ command.setDirectory(directory);
+ command.setInitialBranch("main");
+ command.setInitialBranch(null);
+ try (Git git = command.call()) {
+ Repository r = git.getRepository();
+ assertNotNull(r);
+ assertEquals("refs/heads/master", r.getFullBranch());
+ }
+ }
+
+ @Test
+ public void testInitRepositoryEmptyInitialBranch() throws Exception {
+ File directory = createTempDirectory("testInitRepository");
+ InitCommand command = new InitCommand();
+ command.setDirectory(directory);
+ command.setInitialBranch("main");
+ command.setInitialBranch("");
+ try (Git git = command.call()) {
+ Repository r = git.getRepository();
+ assertNotNull(r);
+ assertEquals("refs/heads/master", r.getFullBranch());
}
}
@@ -72,6 +141,23 @@ public class InitCommandTest extends RepositoryTestCase {
Repository repository = git.getRepository();
assertNotNull(repository);
assertTrue(repository.isBare());
+ assertEquals("refs/heads/master", repository.getFullBranch());
+ }
+ }
+
+ @Test
+ public void testInitBareRepositoryMainInitialBranch()
+ throws IOException, JGitInternalException, GitAPIException {
+ File directory = createTempDirectory("testInitBareRepository");
+ InitCommand command = new InitCommand();
+ command.setDirectory(directory);
+ command.setBare(true);
+ command.setInitialBranch("main");
+ try (Git git = command.call()) {
+ Repository repository = git.getRepository();
+ assertNotNull(repository);
+ assertTrue(repository.isBare());
+ assertEquals("refs/heads/main", repository.getFullBranch());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java
index 6460c7988a..c563d5a47f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java
@@ -232,6 +232,53 @@ public class LogCommandTest extends RepositoryTestCase {
assertFalse(i.hasNext());
}
+ /**
+ * <pre>
+ * A - B - C - M
+ * \ /
+ * -D(side)
+ * </pre>
+ */
+ @Test
+ public void addRangeWithMerge() throws Exception{
+ String fileA = "fileA";
+ String fileB = "fileB";
+ Git git = Git.wrap(db);
+
+ writeTrashFile(fileA, fileA);
+ git.add().addFilepattern(fileA).call();
+ git.commit().setMessage("commit a").call();
+
+ writeTrashFile(fileA, fileA);
+ git.add().addFilepattern(fileA).call();
+ RevCommit b = git.commit().setMessage("commit b").call();
+
+ writeTrashFile(fileA, fileA);
+ git.add().addFilepattern(fileA).call();
+ RevCommit c = git.commit().setMessage("commit c").call();
+
+ createBranch(b, "refs/heads/side");
+ checkoutBranch("refs/heads/side");
+
+ writeTrashFile(fileB, fileB);
+ git.add().addFilepattern(fileB).call();
+ RevCommit d = git.commit().setMessage("commit d").call();
+
+ checkoutBranch("refs/heads/master");
+ MergeResult m = git.merge().include(d.getId()).call();
+ assertEquals(MergeResult.MergeStatus.MERGED, m.getMergeStatus());
+
+ Iterator<RevCommit> rangeLog = git.log().addRange(b.getId(), m.getNewHead()).call().iterator();
+
+ RevCommit commit = rangeLog.next();
+ assertEquals(m.getNewHead(), commit.getId());
+ commit = rangeLog.next();
+ assertEquals(c.getId(), commit.getId());
+ commit = rangeLog.next();
+ assertEquals(d.getId(), commit.getId());
+ assertFalse(rangeLog.hasNext());
+ }
+
private void setCommitsAndMerge() throws Exception {
Git git = Git.wrap(db);
writeTrashFile("file1", "1\n2\n3\n4\n");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
index b1c54b9eff..99034174ba 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, 2013 Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2020 Chris Aniszczyk <caniszczyk@gmail.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -11,6 +11,9 @@ package org.eclipse.jgit.api;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
@@ -19,8 +22,10 @@ import java.util.List;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidTagNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -29,6 +34,59 @@ import org.junit.Test;
public class TagCommandTest extends RepositoryTestCase {
@Test
+ public void testTagKind() {
+ try (Git git = new Git(db)) {
+ assertTrue(git.tag().isAnnotated());
+ assertTrue(git.tag().setSigned(true).isAnnotated());
+ assertTrue(git.tag().setSigned(false).isAnnotated());
+ assertTrue(git.tag().setSigningKey(null).isAnnotated());
+ assertTrue(git.tag().setSigningKey("something").isAnnotated());
+ assertTrue(git.tag().setSigned(false).setSigningKey(null)
+ .isAnnotated());
+ assertTrue(git.tag().setSigned(false).setSigningKey("something")
+ .isAnnotated());
+ assertTrue(git.tag().setSigned(true).setSigningKey(null)
+ .isAnnotated());
+ assertTrue(git.tag().setSigned(true).setSigningKey("something")
+ .isAnnotated());
+ assertTrue(git.tag().setAnnotated(true).isAnnotated());
+ assertTrue(
+ git.tag().setAnnotated(true).setSigned(true).isAnnotated());
+ assertTrue(git.tag().setAnnotated(true).setSigned(false)
+ .isAnnotated());
+ assertTrue(git.tag().setAnnotated(true).setSigningKey(null)
+ .isAnnotated());
+ assertTrue(git.tag().setAnnotated(true).setSigningKey("something")
+ .isAnnotated());
+ assertTrue(git.tag().setAnnotated(true).setSigned(false)
+ .setSigningKey(null).isAnnotated());
+ assertTrue(git.tag().setAnnotated(true).setSigned(false)
+ .setSigningKey("something").isAnnotated());
+ assertTrue(git.tag().setAnnotated(true).setSigned(true)
+ .setSigningKey(null).isAnnotated());
+ assertTrue(git.tag().setAnnotated(true).setSigned(true)
+ .setSigningKey("something").isAnnotated());
+ assertFalse(git.tag().setAnnotated(false).isAnnotated());
+ assertTrue(git.tag().setAnnotated(false).setSigned(true)
+ .isAnnotated());
+ assertFalse(git.tag().setAnnotated(false).setSigned(false)
+ .isAnnotated());
+ assertFalse(git.tag().setAnnotated(false).setSigningKey(null)
+ .isAnnotated());
+ assertTrue(git.tag().setAnnotated(false).setSigningKey("something")
+ .isAnnotated());
+ assertFalse(git.tag().setAnnotated(false).setSigned(false)
+ .setSigningKey(null).isAnnotated());
+ assertTrue(git.tag().setAnnotated(false).setSigned(false)
+ .setSigningKey("something").isAnnotated());
+ assertTrue(git.tag().setAnnotated(false).setSigned(true)
+ .setSigningKey(null).isAnnotated());
+ assertTrue(git.tag().setAnnotated(false).setSigned(true)
+ .setSigningKey("something").isAnnotated());
+ }
+ }
+
+ @Test
public void testTaggingOnHead() throws GitAPIException, IOException {
try (Git git = new Git(db);
RevWalk walk = new RevWalk(db)) {
@@ -67,6 +125,29 @@ public class TagCommandTest extends RepositoryTestCase {
}
@Test
+ public void testForceNoChangeLightweight() throws GitAPIException {
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ RevCommit commit = git.commit().setMessage("second commit").call();
+ git.commit().setMessage("third commit").call();
+ Ref tagRef = git.tag().setObjectId(commit).setName("tag")
+ .setAnnotated(false).call();
+ assertEquals(commit.getId(), tagRef.getObjectId());
+ // Without force, we want to get a RefAlreadyExistsException
+ RefAlreadyExistsException e = assertThrows(
+ RefAlreadyExistsException.class,
+ () -> git.tag().setObjectId(commit).setName("tag")
+ .setAnnotated(false).call());
+ assertEquals(RefUpdate.Result.NO_CHANGE, e.getUpdateResult());
+ // With force the call should work
+ assertEquals(commit.getId(),
+ git.tag().setObjectId(commit).setName("tag")
+ .setAnnotated(false).setForceUpdate(true).call()
+ .getObjectId());
+ }
+ }
+
+ @Test
public void testEmptyTagName() throws GitAPIException {
try (Git git = new Git(db)) {
git.commit().setMessage("initial commit").call();
@@ -93,19 +174,6 @@ public class TagCommandTest extends RepositoryTestCase {
}
}
- @Test
- public void testFailureOnSignedTags() throws GitAPIException {
- try (Git git = new Git(db)) {
- git.commit().setMessage("initial commit").call();
- try {
- git.tag().setSigned(true).setName("tag").call();
- fail("We should have failed with an UnsupportedOperationException due to signed tag");
- } catch (UnsupportedOperationException e) {
- // should hit here
- }
- }
- }
-
private List<Ref> getTags() throws Exception {
return db.getRefDatabase().getRefsByPrefix(R_TAGS);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedObjectReachabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedObjectReachabilityTest.java
index d2b6e89168..f2f74050b9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedObjectReachabilityTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedObjectReachabilityTest.java
@@ -7,11 +7,12 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-package org.eclipse.jgit.revwalk;
+package org.eclipse.jgit.internal.revwalk;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.internal.storage.file.GC;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.revwalk.ObjectReachabilityChecker;
public class BitmappedObjectReachabilityTest
extends ObjectReachabilityTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedReachabilityCheckerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedReachabilityCheckerTest.java
index 2cb88b979e..5833c7a81b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedReachabilityCheckerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedReachabilityCheckerTest.java
@@ -7,13 +7,14 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-package org.eclipse.jgit.revwalk;
+package org.eclipse.jgit.internal.revwalk;
import static org.junit.Assert.assertNotNull;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.internal.storage.file.GC;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.revwalk.ReachabilityChecker;
public class BitmappedReachabilityCheckerTest
extends ReachabilityCheckerTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectReachabilityTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ObjectReachabilityTestCase.java
index 267b163f43..37ff40bdf7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectReachabilityTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ObjectReachabilityTestCase.java
@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-package org.eclipse.jgit.revwalk;
+package org.eclipse.jgit.internal.revwalk;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -20,6 +20,10 @@ import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
+import org.eclipse.jgit.revwalk.ObjectReachabilityChecker;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
import org.junit.Before;
import org.junit.Test;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/PedestrianObjectReachabilityTest.java
index b1c9556df8..f06a768340 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/PedestrianObjectReachabilityTest.java
@@ -7,10 +7,11 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-package org.eclipse.jgit.revwalk;
+package org.eclipse.jgit.internal.revwalk;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.revwalk.ObjectReachabilityChecker;
public class PedestrianObjectReachabilityTest
extends ObjectReachabilityTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianReachabilityCheckerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/PedestrianReachabilityCheckerTest.java
index 3029e056eb..f9d4e182a4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianReachabilityCheckerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/PedestrianReachabilityCheckerTest.java
@@ -7,10 +7,11 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-package org.eclipse.jgit.revwalk;
+package org.eclipse.jgit.internal.revwalk;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.revwalk.ReachabilityChecker;
public class PedestrianReachabilityCheckerTest
extends ReachabilityCheckerTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ReachabilityCheckerTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ReachabilityCheckerTestCase.java
index 4695eaa695..1ff6e7defb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ReachabilityCheckerTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ReachabilityCheckerTestCase.java
@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-package org.eclipse.jgit.revwalk;
+package org.eclipse.jgit.internal.revwalk;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -19,6 +19,8 @@ import java.util.stream.Stream;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.revwalk.ReachabilityChecker;
+import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Test;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackDescriptionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackDescriptionTest.java
index 18cf117174..b2c8ad5e7f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackDescriptionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackDescriptionTest.java
@@ -13,7 +13,6 @@ package org.eclipse.jgit.internal.storage.dfs;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.COMPACT;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC_REST;
-import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC_TXN;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.INSERT;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.RECEIVE;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
@@ -82,7 +81,7 @@ public final class DfsPackDescriptionTest {
DfsPackDescription.objectLookupComparator(
new PackSource.ComparatorBuilder()
.add(GC)
- .add(INSERT, RECEIVE, GC_REST, GC_TXN, UNREACHABLE_GARBAGE)
+ .add(INSERT, RECEIVE, GC_REST, UNREACHABLE_GARBAGE)
.add(COMPACT)
.build()),
a, b);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackSourceTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackSourceTest.java
index 6fcd4ac051..dfd112976c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackSourceTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackSourceTest.java
@@ -14,7 +14,6 @@ import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.CO
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.DEFAULT_COMPARATOR;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC_REST;
-import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC_TXN;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.INSERT;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.RECEIVE;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
@@ -30,7 +29,6 @@ public class PackSourceTest {
assertEquals(0, DEFAULT_COMPARATOR.compare(COMPACT, COMPACT));
assertEquals(0, DEFAULT_COMPARATOR.compare(GC, GC));
assertEquals(0, DEFAULT_COMPARATOR.compare(GC_REST, GC_REST));
- assertEquals(0, DEFAULT_COMPARATOR.compare(GC_TXN, GC_TXN));
assertEquals(0, DEFAULT_COMPARATOR.compare(UNREACHABLE_GARBAGE, UNREACHABLE_GARBAGE));
assertEquals(0, DEFAULT_COMPARATOR.compare(INSERT, RECEIVE));
@@ -47,11 +45,5 @@ public class PackSourceTest {
assertEquals(-1, DEFAULT_COMPARATOR.compare(GC, GC_REST));
assertEquals(1, DEFAULT_COMPARATOR.compare(GC_REST, GC));
-
- assertEquals(-1, DEFAULT_COMPARATOR.compare(GC_REST, GC_TXN));
- assertEquals(1, DEFAULT_COMPARATOR.compare(GC_TXN, GC_REST));
-
- assertEquals(-1, DEFAULT_COMPARATOR.compare(GC_TXN, UNREACHABLE_GARBAGE));
- assertEquals(1, DEFAULT_COMPARATOR.compare(UNREACHABLE_GARBAGE, GC_TXN));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableStackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableStackTest.java
index 72bff16831..6c74f0079a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableStackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableStackTest.java
@@ -18,7 +18,8 @@ import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.util.Arrays;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -90,8 +91,9 @@ public class FileReftableStackTest {
assertEquals(ObjectId.zeroId(), c.getRef().getObjectId());
}
- List<String> files = Arrays.asList(reftableDir.listFiles()).stream()
- .map(File::getName).collect(Collectors.toList());
+ List<String> files = Files.list(reftableDir.toPath())
+ .map(Path::getFileName).map(Path::toString)
+ .collect(Collectors.toList());
Collections.sort(files);
assertTrue(files.size() < 20);
@@ -130,11 +132,14 @@ public class FileReftableStackTest {
});
assertTrue(ok);
- List<File> files = Arrays.asList(reftableDir.listFiles());
+ List<Path> files = Files.list(reftableDir.toPath())
+ .collect(Collectors.toList());
for (int j = 0; j < files.size(); j++) {
- File f = files.get(j);
- if (f.getName().endsWith(".ref")) {
- assertTrue(f.delete());
+ Path f = files.get(j);
+ Path fileName = f.getFileName();
+ if (fileName != null
+ && fileName.toString().endsWith(".ref")) {
+ Files.delete(f);
break outer;
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java
index 33bacbe3e2..15c9109ca0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java
@@ -28,14 +28,18 @@ import java.io.File;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.RefRename;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
@@ -579,6 +583,64 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
assertEquals(Ref.Storage.PACKED, b.getStorage());
}
+ @Test
+ public void testGetRefsExcludingPrefix() throws IOException {
+ Set<String> prefixes = new HashSet<>();
+ prefixes.add("refs/tags");
+ // HEAD + 12 refs/heads are present here.
+ List<Ref> refs =
+ db.getRefDatabase().getRefsByPrefixWithExclusions(RefDatabase.ALL, prefixes);
+ assertEquals(13, refs.size());
+ checkContainsRef(refs, db.exactRef("HEAD"));
+ checkContainsRef(refs, db.exactRef("refs/heads/a"));
+ for (Ref notInResult : db.getRefDatabase().getRefsByPrefix("refs/tags")) {
+ assertFalse(refs.contains(notInResult));
+ }
+ }
+
+ @Test
+ public void testGetRefsExcludingPrefixes() throws IOException {
+ Set<String> exclude = new HashSet<>();
+ exclude.add("refs/tags/");
+ exclude.add("refs/heads/");
+ List<Ref> refs = db.getRefDatabase().getRefsByPrefixWithExclusions(RefDatabase.ALL, exclude);
+ assertEquals(1, refs.size());
+ checkContainsRef(refs, db.exactRef("HEAD"));
+ }
+
+ @Test
+ public void testGetRefsExcludingNonExistingPrefixes() throws IOException {
+ Set<String> exclude = new HashSet<>();
+ exclude.add("refs/tags/");
+ exclude.add("refs/heads/");
+ exclude.add("refs/nonexistent/");
+ List<Ref> refs = db.getRefDatabase().getRefsByPrefixWithExclusions(RefDatabase.ALL, exclude);
+ assertEquals(1, refs.size());
+ checkContainsRef(refs, db.exactRef("HEAD"));
+ }
+
+ @Test
+ public void testGetRefsWithPrefixExcludingPrefixes() throws IOException {
+ Set<String> exclude = new HashSet<>();
+ exclude.add("refs/heads/pa");
+ String include = "refs/heads/p";
+ List<Ref> refs = db.getRefDatabase().getRefsByPrefixWithExclusions(include, exclude);
+ assertEquals(1, refs.size());
+ checkContainsRef(refs, db.exactRef("refs/heads/prefix/a"));
+ }
+
+ @Test
+ public void testGetRefsWithPrefixExcludingOverlappingPrefixes() throws IOException {
+ Set<String> exclude = new HashSet<>();
+ exclude.add("refs/heads/pa");
+ exclude.add("refs/heads/");
+ exclude.add("refs/heads/p");
+ exclude.add("refs/tags/");
+ List<Ref> refs = db.getRefDatabase().getRefsByPrefixWithExclusions(RefDatabase.ALL, exclude);
+ assertEquals(1, refs.size());
+ checkContainsRef(refs, db.exactRef("HEAD"));
+ }
+
private RefUpdate updateRef(String name) throws IOException {
final RefUpdate ref = db.updateRef(name);
ref.setNewObjectId(db.resolve(Constants.HEAD));
@@ -596,4 +658,14 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
fail("link " + src + " to " + dst);
}
}
+
+ private static void checkContainsRef(Collection<Ref> haystack, Ref needle) {
+ for (Ref ref : haystack) {
+ if (ref.getName().equals(needle.getName()) &&
+ ref.getObjectId().equals(needle.getObjectId())) {
+ return;
+ }
+ }
+ fail("list " + haystack + " does not contain ref " + needle);
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
index d007dd4511..42e4238451 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
@@ -157,7 +157,7 @@ public class GcBasicPackingTest extends GcTestCase {
.create();
tr.update("refs/tags/t1", second);
- Collection<PackFile> oldPacks = tr.getRepository().getObjectDatabase()
+ Collection<Pack> oldPacks = tr.getRepository().getObjectDatabase()
.getPacks();
assertEquals(0, oldPacks.size());
stats = gc.getStatistics();
@@ -171,7 +171,7 @@ public class GcBasicPackingTest extends GcTestCase {
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
- List<PackFile> packs = new ArrayList<>(
+ List<Pack> packs = new ArrayList<>(
repo.getObjectDatabase().getPacks());
assertEquals(11, packs.get(0).getObjectCount());
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java
index bb8455f515..5cac1e3429 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java
@@ -156,8 +156,8 @@ public class GcConcurrentTest extends GcTestCase {
}
}
- PackFile getSinglePack(FileRepository r) {
- Collection<PackFile> packs = r.getObjectDatabase().getPacks();
+ Pack getSinglePack(FileRepository r) {
+ Collection<Pack> packs = r.getObjectDatabase().getPacks();
assertEquals(1, packs.size());
return packs.iterator().next();
}
@@ -206,11 +206,11 @@ public class GcConcurrentTest extends GcTestCase {
SampleDataRepositoryTestCase.copyCGitTestPacks(repo);
ExecutorService executor = Executors.newSingleThreadExecutor();
final CountDownLatch latch = new CountDownLatch(1);
- Future<Collection<PackFile>> result = executor.submit(() -> {
+ Future<Collection<Pack>> result = executor.submit(() -> {
long start = System.currentTimeMillis();
System.out.println("starting gc");
latch.countDown();
- Collection<PackFile> r = gc.gc();
+ Collection<Pack> r = gc.gc();
System.out.println(
"gc took " + (System.currentTimeMillis() - start) + " ms");
return r;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java
index e1559584fd..8472983d5b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java
@@ -36,9 +36,9 @@ public class GcKeepFilesTest extends GcTestCase {
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
- Iterator<PackFile> packIt = repo.getObjectDatabase().getPacks()
+ Iterator<Pack> packIt = repo.getObjectDatabase().getPacks()
.iterator();
- PackFile singlePack = packIt.next();
+ Pack singlePack = packIt.next();
assertFalse(packIt.hasNext());
String packFileName = singlePack.getPackFile().getPath();
String keepFileName = packFileName.substring(0,
@@ -58,7 +58,7 @@ public class GcKeepFilesTest extends GcTestCase {
assertEquals(2, stats.numberOfPackFiles);
// check that no object is packed twice
- Iterator<PackFile> packs = repo.getObjectDatabase().getPacks()
+ Iterator<Pack> packs = repo.getObjectDatabase().getPacks()
.iterator();
PackIndex ind1 = packs.next().getIndex();
assertEquals(4, ind1.getObjectCount());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java
index 796df3db06..7386621204 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java
@@ -12,6 +12,7 @@ package org.eclipse.jgit.internal.storage.file;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
@@ -89,6 +90,7 @@ public class GcPruneNonReferencedTest extends GcTestCase {
private void assertNoEmptyFanoutDirectories() {
File[] fanout = repo.getObjectsDirectory().listFiles();
+ assertNotNull(fanout);
for (File f : fanout) {
if (f.isDirectory()) {
String[] entries = f.list();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java
index ac65c33621..7c32ce7cea 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java
@@ -11,6 +11,7 @@ package org.eclipse.jgit.internal.storage.file;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
@@ -71,14 +72,14 @@ public class PackFileSnapshotTest extends RepositoryTestCase {
c.setInt(ConfigConstants.CONFIG_GC_SECTION, null,
ConfigConstants.CONFIG_KEY_AUTOPACKLIMIT, 1);
c.save();
- Collection<PackFile> packs = gc(Deflater.NO_COMPRESSION);
+ Collection<Pack> packs = gc(Deflater.NO_COMPRESSION);
assertEquals("expected 1 packfile after gc", 1, packs.size());
- PackFile p1 = packs.iterator().next();
+ Pack p1 = packs.iterator().next();
PackFileSnapshot snapshot = p1.getFileSnapshot();
packs = gc(Deflater.BEST_COMPRESSION);
assertEquals("expected 1 packfile after gc", 1, packs.size());
- PackFile p2 = packs.iterator().next();
+ Pack p2 = packs.iterator().next();
File pf = p2.getPackFile();
// changing compression level with aggressive gc may change size,
@@ -152,11 +153,11 @@ public class PackFileSnapshotTest extends RepositoryTestCase {
createTestRepo(testDataSeed, testDataLength);
// repack to create initial packfile
- PackFile pf = repackAndCheck(5, null, null, null);
- Path packFilePath = pf.getPackFile().toPath();
- AnyObjectId chk1 = pf.getPackChecksum();
- String name = pf.getPackName();
- Long length = Long.valueOf(pf.getPackFile().length());
+ Pack p = repackAndCheck(5, null, null, null);
+ Path packFilePath = p.getPackFile().toPath();
+ AnyObjectId chk1 = p.getPackChecksum();
+ String name = p.getPackName();
+ Long length = Long.valueOf(p.getPackFile().length());
FS fs = db.getFS();
Instant m1 = fs.lastModifiedInstant(packFilePath);
@@ -206,13 +207,16 @@ public class PackFileSnapshotTest extends RepositoryTestCase {
createTestRepo(testDataSeed, testDataLength);
// Repack to create initial packfile. Make a copy of it
- PackFile pf = repackAndCheck(5, null, null, null);
- Path packFilePath = pf.getPackFile().toPath();
- Path packFileBasePath = packFilePath.resolveSibling(
- packFilePath.getFileName().toString().replaceAll(".pack", ""));
- AnyObjectId chk1 = pf.getPackChecksum();
- String name = pf.getPackName();
- Long length = Long.valueOf(pf.getPackFile().length());
+ Pack p = repackAndCheck(5, null, null, null);
+ Path packFilePath = p.getPackFile().toPath();
+ Path fn = packFilePath.getFileName();
+ assertNotNull(fn);
+ String packFileName = fn.toString();
+ Path packFileBasePath = packFilePath
+ .resolveSibling(packFileName.replaceAll(".pack", ""));
+ AnyObjectId chk1 = p.getPackChecksum();
+ String name = p.getPackName();
+ Long length = Long.valueOf(p.getPackFile().length());
copyPack(packFileBasePath, "", ".copy1");
// Repack to create second packfile. Make a copy of it
@@ -276,10 +280,10 @@ public class PackFileSnapshotTest extends RepositoryTestCase {
Paths.get(base + ".pack" + dstSuffix));
}
- private PackFile repackAndCheck(int compressionLevel, String oldName,
+ private Pack repackAndCheck(int compressionLevel, String oldName,
Long oldLength, AnyObjectId oldChkSum)
throws IOException, ParseException {
- PackFile p = getSinglePack(gc(compressionLevel));
+ Pack p = getSinglePack(gc(compressionLevel));
File pf = p.getPackFile();
// The following two assumptions should not cause the test to fail. If
// on a certain platform we get packfiles (containing the same git
@@ -294,14 +298,14 @@ public class PackFileSnapshotTest extends RepositoryTestCase {
return p;
}
- private PackFile getSinglePack(Collection<PackFile> packs) {
- Iterator<PackFile> pIt = packs.iterator();
- PackFile p = pIt.next();
+ private Pack getSinglePack(Collection<Pack> packs) {
+ Iterator<Pack> pIt = packs.iterator();
+ Pack p = pIt.next();
assertFalse(pIt.hasNext());
return p;
}
- private Collection<PackFile> gc(int compressionLevel)
+ private Collection<Pack> gc(int compressionLevel)
throws IOException, ParseException {
GC gc = new GC(db);
PackConfig pc = new PackConfig(db.getConfig());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java
index 8c56480fe1..85043034aa 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java
@@ -160,7 +160,7 @@ public class PackInserterTest extends RepositoryTestCase {
}
assertPacksOnly();
- List<PackFile> packs = listPacks();
+ List<Pack> packs = listPacks();
assertEquals(1, packs.size());
assertEquals(3, packs.get(0).getObjectCount());
@@ -193,7 +193,7 @@ public class PackInserterTest extends RepositoryTestCase {
}
assertPacksOnly();
- List<PackFile> packs = listPacks();
+ List<Pack> packs = listPacks();
assertEquals(2, packs.size());
assertEquals(1, packs.get(0).getObjectCount());
assertEquals(1, packs.get(1).getObjectCount());
@@ -216,9 +216,9 @@ public class PackInserterTest extends RepositoryTestCase {
}
assertPacksOnly();
- Collection<PackFile> packs = listPacks();
+ Collection<Pack> packs = listPacks();
assertEquals(1, packs.size());
- PackFile p = packs.iterator().next();
+ Pack p = packs.iterator().next();
assertEquals(1, p.getObjectCount());
try (ObjectReader reader = db.newObjectReader()) {
@@ -237,9 +237,9 @@ public class PackInserterTest extends RepositoryTestCase {
}
assertPacksOnly();
- List<PackFile> packs = listPacks();
+ List<Pack> packs = listPacks();
assertEquals(1, packs.size());
- PackFile pack = packs.get(0);
+ Pack pack = packs.get(0);
assertEquals(1, pack.getObjectCount());
String inode = getInode(pack.getPackFile());
@@ -372,7 +372,7 @@ public class PackInserterTest extends RepositoryTestCase {
}
assertPacksOnly();
- List<PackFile> packs = listPacks();
+ List<Pack> packs = listPacks();
assertEquals(1, packs.size());
assertEquals(2, packs.get(0).getObjectCount());
@@ -489,16 +489,16 @@ public class PackInserterTest extends RepositoryTestCase {
}
}
- private List<PackFile> listPacks() throws Exception {
- List<PackFile> fromOpenDb = listPacks(db);
- List<PackFile> reopened;
+ private List<Pack> listPacks() throws Exception {
+ List<Pack> fromOpenDb = listPacks(db);
+ List<Pack> reopened;
try (FileRepository db2 = new FileRepository(db.getDirectory())) {
reopened = listPacks(db2);
}
assertEquals(fromOpenDb.size(), reopened.size());
for (int i = 0 ; i < fromOpenDb.size(); i++) {
- PackFile a = fromOpenDb.get(i);
- PackFile b = reopened.get(i);
+ Pack a = fromOpenDb.get(i);
+ Pack b = reopened.get(i);
assertEquals(a.getPackName(), b.getPackName());
assertEquals(
a.getPackFile().getAbsolutePath(), b.getPackFile().getAbsolutePath());
@@ -508,9 +508,9 @@ public class PackInserterTest extends RepositoryTestCase {
return fromOpenDb;
}
- private static List<PackFile> listPacks(FileRepository db) throws Exception {
+ private static List<Pack> listPacks(FileRepository db) throws Exception {
return db.getObjectDatabase().getPacks().stream()
- .sorted(comparing(PackFile::getPackName)).collect(toList());
+ .sorted(comparing(Pack::getPackName)).collect(toList());
}
private PackInserter newInserter() {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackTest.java
index 97a86e249e..182e422650 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackTest.java
@@ -57,7 +57,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class PackFileTest extends LocalDiskRepositoryTestCase {
+public class PackTest extends LocalDiskRepositoryTestCase {
private int streamThreshold = 16 * 1024;
private TestRng rng;
@@ -228,21 +228,21 @@ public class PackFileTest extends LocalDiskRepositoryTestCase {
PackedObjectInfo a = new PackedObjectInfo(idA);
PackedObjectInfo b = new PackedObjectInfo(idB);
- TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64 * 1024);
- packHeader(pack, 2);
- a.setOffset(pack.length());
- objectHeader(pack, Constants.OBJ_BLOB, base.length);
- deflate(pack, base);
+ TemporaryBuffer.Heap packContents = new TemporaryBuffer.Heap(64 * 1024);
+ packHeader(packContents, 2);
+ a.setOffset(packContents.length());
+ objectHeader(packContents, Constants.OBJ_BLOB, base.length);
+ deflate(packContents, base);
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
DeltaEncoder de = new DeltaEncoder(tmp, base.length, 3L << 30);
de.copy(0, 1);
byte[] delta = tmp.toByteArray();
- b.setOffset(pack.length());
- objectHeader(pack, Constants.OBJ_REF_DELTA, delta.length);
- idA.copyRawTo(pack);
- deflate(pack, delta);
- byte[] footer = digest(pack);
+ b.setOffset(packContents.length());
+ objectHeader(packContents, Constants.OBJ_REF_DELTA, delta.length);
+ idA.copyRawTo(packContents);
+ deflate(packContents, delta);
+ byte[] footer = digest(packContents);
File dir = new File(repo.getObjectDatabase().getDirectory(),
"pack");
@@ -250,7 +250,7 @@ public class PackFileTest extends LocalDiskRepositoryTestCase {
File idxName = new File(dir, idA.name() + ".idx");
try (FileOutputStream f = new FileOutputStream(packName)) {
- f.write(pack.toByteArray());
+ f.write(packContents.toByteArray());
}
try (FileOutputStream f = new FileOutputStream(idxName)) {
@@ -261,14 +261,14 @@ public class PackFileTest extends LocalDiskRepositoryTestCase {
new PackIndexWriterV1(f).write(list, footer);
}
- PackFile packFile = new PackFile(packName, PackExt.INDEX.getBit());
+ Pack pack = new Pack(packName, PackExt.INDEX.getBit());
try {
- packFile.get(wc, b);
+ pack.get(wc, b);
fail("expected LargeObjectException.ExceedsByteArrayLimit");
} catch (LargeObjectException.ExceedsByteArrayLimit bad) {
assertNull(bad.getObjectId());
} finally {
- packFile.close();
+ pack.close();
}
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
index c90310e079..214ddb9893 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
@@ -72,7 +72,7 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
private ByteArrayOutputStream os;
- private PackFile pack;
+ private Pack pack;
private ObjectInserter inserter;
@@ -840,7 +840,7 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
p.setAllowThin(thin);
p.setIndexVersion(2);
p.parse(NullProgressMonitor.INSTANCE);
- pack = p.getPackFile();
+ pack = p.getPack();
assertNotNull("have PackFile after parsing", pack);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
index 97ef5993b9..38c545ef57 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
@@ -30,8 +30,10 @@ import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -353,6 +355,24 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
}
@Test
+ public void testGetRefs_ExcludingPrefixes() throws IOException {
+ writeLooseRef("refs/heads/A", A);
+ writeLooseRef("refs/heads/B", B);
+ writeLooseRef("refs/tags/tag", A);
+ writeLooseRef("refs/something/something", B);
+ writeLooseRef("refs/aaa/aaa", A);
+
+ Set<String> toExclude = new HashSet<>();
+ toExclude.add("refs/aaa/");
+ toExclude.add("refs/heads/");
+ List<Ref> refs = refdir.getRefsByPrefixWithExclusions(RefDatabase.ALL, toExclude);
+
+ assertEquals(2, refs.size());
+ assertTrue(refs.contains(refdir.exactRef("refs/tags/tag")));
+ assertTrue(refs.contains(refdir.exactRef("refs/something/something")));
+ }
+
+ @Test
public void testFirstExactRef_IgnoresGarbageRef() throws IOException {
writeLooseRef("refs/heads/A", A);
write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java
index ee4c9b1dc7..8f1371e09c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java
@@ -32,8 +32,8 @@ public class T0004_PackReaderTest extends SampleDataRepositoryTestCase {
final ObjectId id;
final ObjectLoader or;
- PackFile pr = null;
- for (PackFile p : db.getObjectDatabase().getPacks()) {
+ Pack pr = null;
+ for (Pack p : db.getObjectDatabase().getPacks()) {
if (PACK_NAME.equals(p.getPackName())) {
pr = p;
break;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
index 0a03fc3523..9aea3b4b25 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
@@ -138,6 +138,118 @@ public class MergedReftableTest {
}
@Test
+ public void twoTableSeekPastWithRefCursor() throws IOException {
+ List<Ref> delta1 = Arrays.asList(
+ ref("refs/heads/apple", 1),
+ ref("refs/heads/master", 2));
+ List<Ref> delta2 = Arrays.asList(
+ ref("refs/heads/banana", 3),
+ ref("refs/heads/zzlast", 4));
+
+ MergedReftable mr = merge(write(delta1), write(delta2));
+ try (RefCursor rc = mr.seekRefsWithPrefix("")) {
+ assertTrue(rc.next());
+ assertEquals("refs/heads/apple", rc.getRef().getName());
+ assertEquals(id(1), rc.getRef().getObjectId());
+
+ rc.seekPastPrefix("refs/heads/banana/");
+
+ assertTrue(rc.next());
+ assertEquals("refs/heads/master", rc.getRef().getName());
+ assertEquals(id(2), rc.getRef().getObjectId());
+
+ assertTrue(rc.next());
+ assertEquals("refs/heads/zzlast", rc.getRef().getName());
+ assertEquals(id(4), rc.getRef().getObjectId());
+
+ assertEquals(1, rc.getRef().getUpdateIndex());
+ }
+ }
+
+ @Test
+ public void oneTableSeekPastWithRefCursor() throws IOException {
+ List<Ref> delta1 = Arrays.asList(
+ ref("refs/heads/apple", 1),
+ ref("refs/heads/master", 2));
+
+ MergedReftable mr = merge(write(delta1));
+ try (RefCursor rc = mr.seekRefsWithPrefix("")) {
+ rc.seekPastPrefix("refs/heads/apple");
+
+ assertTrue(rc.next());
+ assertEquals("refs/heads/master", rc.getRef().getName());
+ assertEquals(id(2), rc.getRef().getObjectId());
+
+ assertEquals(1, rc.getRef().getUpdateIndex());
+ }
+ }
+
+ @Test
+ public void seekPastToNonExistentPrefixToTheMiddle() throws IOException {
+ List<Ref> delta1 = Arrays.asList(
+ ref("refs/heads/apple", 1),
+ ref("refs/heads/master", 2));
+ List<Ref> delta2 = Arrays.asList(
+ ref("refs/heads/banana", 3),
+ ref("refs/heads/zzlast", 4));
+
+ MergedReftable mr = merge(write(delta1), write(delta2));
+ try (RefCursor rc = mr.seekRefsWithPrefix("")) {
+ rc.seekPastPrefix("refs/heads/x");
+
+ assertTrue(rc.next());
+ assertEquals("refs/heads/zzlast", rc.getRef().getName());
+ assertEquals(id(4), rc.getRef().getObjectId());
+
+ assertEquals(1, rc.getRef().getUpdateIndex());
+ }
+ }
+
+ @Test
+ public void seekPastToNonExistentPrefixToTheEnd() throws IOException {
+ List<Ref> delta1 = Arrays.asList(
+ ref("refs/heads/apple", 1),
+ ref("refs/heads/master", 2));
+ List<Ref> delta2 = Arrays.asList(
+ ref("refs/heads/banana", 3),
+ ref("refs/heads/zzlast", 4));
+
+ MergedReftable mr = merge(write(delta1), write(delta2));
+ try (RefCursor rc = mr.seekRefsWithPrefix("")) {
+ rc.seekPastPrefix("refs/heads/zzz");
+ assertFalse(rc.next());
+ }
+ }
+
+ @Test
+ public void seekPastManyTimes() throws IOException {
+ List<Ref> delta1 = Arrays.asList(
+ ref("refs/heads/apple", 1),
+ ref("refs/heads/master", 2));
+ List<Ref> delta2 = Arrays.asList(
+ ref("refs/heads/banana", 3),
+ ref("refs/heads/zzlast", 4));
+
+ MergedReftable mr = merge(write(delta1), write(delta2));
+ try (RefCursor rc = mr.seekRefsWithPrefix("")) {
+ rc.seekPastPrefix("refs/heads/apple");
+ rc.seekPastPrefix("refs/heads/banana");
+ rc.seekPastPrefix("refs/heads/master");
+ rc.seekPastPrefix("refs/heads/zzlast");
+ assertFalse(rc.next());
+ }
+ }
+
+ @Test
+ public void seekPastOnEmptyTable() throws IOException {
+ MergedReftable mr = merge(write(), write());
+ try (RefCursor rc = mr.seekRefsWithPrefix("")) {
+ rc.seekPastPrefix("refs/");
+ assertFalse(rc.next());
+ }
+ }
+
+ @Test
public void twoTableById() throws IOException {
List<Ref> delta1 = Arrays.asList(
ref("refs/heads/apple", 1),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
index 009914b35c..0d739b9276 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
@@ -10,6 +10,7 @@
package org.eclipse.jgit.internal.storage.reftable;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.HEAD;
import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
@@ -49,8 +50,16 @@ import org.hamcrest.Matchers;
import org.junit.Test;
public class ReftableTest {
+ private static final byte[] LAST_UTF8_CHAR = new byte[] {
+ (byte)0x10,
+ (byte)0xFF,
+ (byte)0xFF};
+
private static final String MASTER = "refs/heads/master";
private static final String NEXT = "refs/heads/next";
+ private static final String AFTER_NEXT = "refs/heads/nextnext";
+ private static final String LAST = "refs/heads/nextnextnext";
+ private static final String NOT_REF_HEADS = "refs/zzz/zzz";
private static final String V1_0 = "refs/tags/v1.0";
private Stats stats;
@@ -396,6 +405,135 @@ public class ReftableTest {
}
@Test
+ public void seekPastRefWithRefCursor() throws IOException {
+ Ref exp = ref(MASTER, 1);
+ Ref next = ref(NEXT, 2);
+ Ref afterNext = ref(AFTER_NEXT, 3);
+ Ref afterNextNext = ref(LAST, 4);
+ ReftableReader t = read(write(exp, next, afterNext, afterNextNext));
+ try (RefCursor rc = t.seekRefsWithPrefix("")) {
+ assertTrue(rc.next());
+ assertEquals(MASTER, rc.getRef().getName());
+
+ rc.seekPastPrefix("refs/heads/next/");
+
+ assertTrue(rc.next());
+ assertEquals(AFTER_NEXT, rc.getRef().getName());
+ assertTrue(rc.next());
+ assertEquals(LAST, rc.getRef().getName());
+
+ assertFalse(rc.next());
+ }
+ }
+
+ @Test
+ public void seekPastToNonExistentPrefixToTheMiddle() throws IOException {
+ Ref exp = ref(MASTER, 1);
+ Ref next = ref(NEXT, 2);
+ Ref afterNext = ref(AFTER_NEXT, 3);
+ Ref afterNextNext = ref(LAST, 4);
+ ReftableReader t = read(write(exp, next, afterNext, afterNextNext));
+ try (RefCursor rc = t.seekRefsWithPrefix("")) {
+ rc.seekPastPrefix("refs/heads/master_non_existent");
+
+ assertTrue(rc.next());
+ assertEquals(NEXT, rc.getRef().getName());
+
+ assertTrue(rc.next());
+ assertEquals(AFTER_NEXT, rc.getRef().getName());
+
+ assertTrue(rc.next());
+ assertEquals(LAST, rc.getRef().getName());
+
+ assertFalse(rc.next());
+ }
+ }
+
+ @Test
+ public void seekPastToNonExistentPrefixToTheEnd() throws IOException {
+ Ref exp = ref(MASTER, 1);
+ Ref next = ref(NEXT, 2);
+ Ref afterNext = ref(AFTER_NEXT, 3);
+ Ref afterNextNext = ref(LAST, 4);
+ ReftableReader t = read(write(exp, next, afterNext, afterNextNext));
+ try (RefCursor rc = t.seekRefsWithPrefix("")) {
+ rc.seekPastPrefix("refs/heads/nextnon_existent_end");
+ assertFalse(rc.next());
+ }
+ }
+
+ @Test
+ public void seekPastWithSeekRefsWithPrefix() throws IOException {
+ Ref exp = ref(MASTER, 1);
+ Ref next = ref(NEXT, 2);
+ Ref afterNext = ref(AFTER_NEXT, 3);
+ Ref afterNextNext = ref(LAST, 4);
+ Ref notRefsHeads = ref(NOT_REF_HEADS, 5);
+ ReftableReader t = read(write(exp, next, afterNext, afterNextNext, notRefsHeads));
+ try (RefCursor rc = t.seekRefsWithPrefix("refs/heads/")) {
+ rc.seekPastPrefix("refs/heads/next/");
+ assertTrue(rc.next());
+ assertEquals(AFTER_NEXT, rc.getRef().getName());
+ assertTrue(rc.next());
+ assertEquals(LAST, rc.getRef().getName());
+
+ // NOT_REF_HEADS is next, but it's omitted because of
+ // seekRefsWithPrefix("refs/heads/").
+ assertFalse(rc.next());
+ }
+ }
+
+ @Test
+ public void seekPastWithLotsOfRefs() throws IOException {
+ Ref[] refs = new Ref[500];
+ for (int i = 1; i <= 500; i++) {
+ refs[i - 1] = ref(String.format("refs/%d", Integer.valueOf(i)), i);
+ }
+ ReftableReader t = read(write(refs));
+ try (RefCursor rc = t.allRefs()) {
+ rc.seekPastPrefix("refs/3");
+ assertTrue(rc.next());
+ assertEquals("refs/4", rc.getRef().getName());
+ assertTrue(rc.next());
+ assertEquals("refs/40", rc.getRef().getName());
+
+ rc.seekPastPrefix("refs/8");
+ assertTrue(rc.next());
+ assertEquals("refs/9", rc.getRef().getName());
+ assertTrue(rc.next());
+ assertEquals("refs/90", rc.getRef().getName());
+ assertTrue(rc.next());
+ assertEquals("refs/91", rc.getRef().getName());
+ }
+ }
+
+ @Test
+ public void seekPastManyTimes() throws IOException {
+ Ref exp = ref(MASTER, 1);
+ Ref next = ref(NEXT, 2);
+ Ref afterNext = ref(AFTER_NEXT, 3);
+ Ref afterNextNext = ref(LAST, 4);
+ ReftableReader t = read(write(exp, next, afterNext, afterNextNext));
+
+ try (RefCursor rc = t.seekRefsWithPrefix("")) {
+ rc.seekPastPrefix("refs/heads/master");
+ rc.seekPastPrefix("refs/heads/next");
+ rc.seekPastPrefix("refs/heads/nextnext");
+ rc.seekPastPrefix("refs/heads/nextnextnext");
+ assertFalse(rc.next());
+ }
+ }
+
+ @Test
+ public void seekPastOnEmptyTable() throws IOException {
+ ReftableReader t = read(write());
+ try (RefCursor rc = t.seekRefsWithPrefix("")) {
+ rc.seekPastPrefix("refs/");
+ assertFalse(rc.next());
+ }
+ }
+
+ @Test
public void indexScan() throws IOException {
List<Ref> refs = new ArrayList<>();
for (int i = 1; i <= 5670; i++) {
@@ -874,6 +1012,14 @@ public class ReftableTest {
}
@Test
+ public void byObjectIdSkipPastPrefix() throws IOException {
+ ReftableReader t = read(write());
+ try (RefCursor rc = t.byObjectId(id(2))) {
+ assertThrows(UnsupportedOperationException.class, () -> rc.seekPastPrefix("refs/heads/"));
+ }
+ }
+
+ @Test
public void unpeeledDoesNotWrite() {
try {
write(new ObjectIdRef.Unpeeled(PACKED, MASTER, id(1)));
@@ -884,6 +1030,18 @@ public class ReftableTest {
}
@Test
+ public void skipPastRefWithLastUTF8() throws IOException {
+ ReftableReader t = read(write(ref(String.format("refs/heads/%sbla", new String(LAST_UTF8_CHAR
+ , UTF_8)), 1)));
+
+ try (RefCursor rc = t.allRefs()) {
+ rc.seekPastPrefix("refs/heads/");
+ assertFalse(rc.next());
+ }
+ }
+
+
+ @Test
public void nameTooLongDoesNotWrite() throws IOException {
try {
ReftableConfig cfg = new ReftableConfig();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java
deleted file mode 100644
index 86016d8f76..0000000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2016 Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.storage.reftree;
-
-import static org.eclipse.jgit.lib.Constants.HEAD;
-import static org.eclipse.jgit.lib.Constants.MASTER;
-import static org.eclipse.jgit.lib.Constants.ORIG_HEAD;
-import static org.eclipse.jgit.lib.Constants.R_HEADS;
-import static org.eclipse.jgit.lib.RefDatabase.ALL;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
-import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDatabase;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
-import org.junit.Before;
-import org.junit.Test;
-
-public class LocalDiskRefTreeDatabaseTest extends LocalDiskRepositoryTestCase {
- private FileRepository repo;
- private RefTreeDatabase refdb;
- private RefDatabase bootstrap;
-
- private TestRepository<FileRepository> testRepo;
- private RevCommit A;
- private RevCommit B;
-
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- FileRepository init = createWorkRepository();
- FileBasedConfig cfg = init.getConfig();
- cfg.setInt("core", null, "repositoryformatversion", 1);
- cfg.setString("extensions", null, "refStorage", "reftree");
- cfg.save();
-
- repo = (FileRepository) new FileRepositoryBuilder()
- .setGitDir(init.getDirectory())
- .build();
- refdb = (RefTreeDatabase) repo.getRefDatabase();
- bootstrap = refdb.getBootstrap();
- addRepoToClose(repo);
-
- RefUpdate head = refdb.newUpdate(HEAD, true);
- head.link(R_HEADS + MASTER);
-
- testRepo = new TestRepository<>(init);
- A = testRepo.commit().create();
- B = testRepo.commit(testRepo.getRevWalk().parseCommit(A));
- }
-
- @Test
- public void testHeadOrigHead() throws IOException {
- RefUpdate master = refdb.newUpdate(HEAD, false);
- master.setExpectedOldObjectId(ObjectId.zeroId());
- master.setNewObjectId(A);
- assertEquals(RefUpdate.Result.NEW, master.update());
- assertEquals(A, refdb.exactRef(HEAD).getObjectId());
-
- RefUpdate orig = refdb.newUpdate(ORIG_HEAD, true);
- orig.setNewObjectId(B);
- assertEquals(RefUpdate.Result.NEW, orig.update());
-
- File origFile = new File(repo.getDirectory(), ORIG_HEAD);
- assertEquals(B.name() + '\n', read(origFile));
- assertEquals(B, bootstrap.exactRef(ORIG_HEAD).getObjectId());
- assertEquals(B, refdb.exactRef(ORIG_HEAD).getObjectId());
- assertFalse(refdb.getRefs(ALL).containsKey(ORIG_HEAD));
-
- List<Ref> addl = refdb.getAdditionalRefs();
- assertEquals(2, addl.size());
- assertEquals(ORIG_HEAD, addl.get(1).getName());
- assertEquals(B, addl.get(1).getObjectId());
- }
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
deleted file mode 100644
index ecee5e5d2b..0000000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
+++ /dev/null
@@ -1,689 +0,0 @@
-/*
- * Copyright (C) 2010, 2013, 2016 Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.storage.reftree;
-
-import static org.eclipse.jgit.lib.Constants.HEAD;
-import static org.eclipse.jgit.lib.Constants.ORIG_HEAD;
-import static org.eclipse.jgit.lib.Constants.R_HEADS;
-import static org.eclipse.jgit.lib.Constants.R_TAGS;
-import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
-import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
-import static org.eclipse.jgit.lib.RefDatabase.ALL;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.LOCK_FAILURE;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
-import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
-import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDatabase;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.SymbolicRef;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevTag;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.junit.Before;
-import org.junit.Test;
-
-public class RefTreeDatabaseTest {
- private InMemRefTreeRepo repo;
- private RefTreeDatabase refdb;
- private RefDatabase bootstrap;
-
- private TestRepository<InMemRefTreeRepo> testRepo;
- private RevCommit A;
- private RevCommit B;
- private RevTag v1_0;
-
- @Before
- public void setUp() throws Exception {
- repo = new InMemRefTreeRepo(new DfsRepositoryDescription("test"));
- bootstrap = refdb.getBootstrap();
-
- testRepo = new TestRepository<>(repo);
- A = testRepo.commit().create();
- B = testRepo.commit(testRepo.getRevWalk().parseCommit(A));
- v1_0 = testRepo.tag("v1_0", B);
- testRepo.getRevWalk().parseBody(v1_0);
- }
-
- @Test
- public void testSupportsAtomic() {
- assertTrue(refdb.performsAtomicTransactions());
- }
-
- @Test
- public void testGetRefs_EmptyDatabase() throws IOException {
- assertTrue("no references", refdb.getRefs(ALL).isEmpty());
- assertTrue("no references", refdb.getRefs(R_HEADS).isEmpty());
- assertTrue("no references", refdb.getRefs(R_TAGS).isEmpty());
- assertTrue("no references", refdb.getAdditionalRefs().isEmpty());
- }
-
- @Test
- public void testGetAdditionalRefs() throws IOException {
- update("refs/heads/master", A);
-
- List<Ref> addl = refdb.getAdditionalRefs();
- assertEquals(1, addl.size());
- assertEquals("refs/txn/committed", addl.get(0).getName());
- assertEquals(getTxnCommitted(), addl.get(0).getObjectId());
- }
-
- @Test
- public void testGetRefs_HeadOnOneBranch() throws IOException {
- symref(HEAD, "refs/heads/master");
- update("refs/heads/master", A);
-
- Map<String, Ref> all = refdb.getRefs(ALL);
- assertEquals(2, all.size());
- assertTrue("has HEAD", all.containsKey(HEAD));
- assertTrue("has master", all.containsKey("refs/heads/master"));
-
- Ref head = all.get(HEAD);
- Ref master = all.get("refs/heads/master");
-
- assertEquals(HEAD, head.getName());
- assertTrue(head.isSymbolic());
- assertSame(LOOSE, head.getStorage());
- assertSame("uses same ref as target", master, head.getTarget());
-
- assertEquals("refs/heads/master", master.getName());
- assertFalse(master.isSymbolic());
- assertSame(PACKED, master.getStorage());
- assertEquals(A, master.getObjectId());
- }
-
- @Test
- public void testGetRefs_DetachedHead() throws IOException {
- update(HEAD, A);
-
- Map<String, Ref> all = refdb.getRefs(ALL);
- assertEquals(1, all.size());
- assertTrue("has HEAD", all.containsKey(HEAD));
-
- Ref head = all.get(HEAD);
- assertEquals(HEAD, head.getName());
- assertFalse(head.isSymbolic());
- assertSame(PACKED, head.getStorage());
- assertEquals(A, head.getObjectId());
- }
-
- @Test
- public void testGetRefs_DeeplyNestedBranch() throws IOException {
- String name = "refs/heads/a/b/c/d/e/f/g/h/i/j/k";
- update(name, A);
-
- Map<String, Ref> all = refdb.getRefs(ALL);
- assertEquals(1, all.size());
-
- Ref r = all.get(name);
- assertEquals(name, r.getName());
- assertFalse(r.isSymbolic());
- assertSame(PACKED, r.getStorage());
- assertEquals(A, r.getObjectId());
- }
-
- @Test
- public void testGetRefs_HeadBranchNotBorn() throws IOException {
- update("refs/heads/A", A);
- update("refs/heads/B", B);
-
- Map<String, Ref> all = refdb.getRefs(ALL);
- assertEquals(2, all.size());
- assertFalse("no HEAD", all.containsKey(HEAD));
-
- Ref a = all.get("refs/heads/A");
- Ref b = all.get("refs/heads/B");
-
- assertEquals(A, a.getObjectId());
- assertEquals(B, b.getObjectId());
-
- assertEquals("refs/heads/A", a.getName());
- assertEquals("refs/heads/B", b.getName());
- }
-
- @Test
- public void testGetRefs_HeadsOnly() throws IOException {
- update("refs/heads/A", A);
- update("refs/heads/B", B);
- update("refs/tags/v1.0", v1_0);
-
- Map<String, Ref> heads = refdb.getRefs(R_HEADS);
- assertEquals(2, heads.size());
-
- Ref a = heads.get("A");
- Ref b = heads.get("B");
-
- assertEquals("refs/heads/A", a.getName());
- assertEquals("refs/heads/B", b.getName());
-
- assertEquals(A, a.getObjectId());
- assertEquals(B, b.getObjectId());
- }
-
- @Test
- public void testGetRefs_TagsOnly() throws IOException {
- update("refs/heads/A", A);
- update("refs/heads/B", B);
- update("refs/tags/v1.0", v1_0);
-
- Map<String, Ref> tags = refdb.getRefs(R_TAGS);
- assertEquals(1, tags.size());
-
- Ref a = tags.get("v1.0");
- assertEquals("refs/tags/v1.0", a.getName());
- assertEquals(v1_0, a.getObjectId());
- assertTrue(a.isPeeled());
- assertEquals(v1_0.getObject(), a.getPeeledObjectId());
- }
-
- @Test
- public void testGetRefs_HeadsSymref() throws IOException {
- symref("refs/heads/other", "refs/heads/master");
- update("refs/heads/master", A);
-
- Map<String, Ref> heads = refdb.getRefs(R_HEADS);
- assertEquals(2, heads.size());
-
- Ref master = heads.get("master");
- Ref other = heads.get("other");
-
- assertEquals("refs/heads/master", master.getName());
- assertEquals(A, master.getObjectId());
-
- assertEquals("refs/heads/other", other.getName());
- assertEquals(A, other.getObjectId());
- assertSame(master, other.getTarget());
- }
-
- @Test
- public void testGetRefs_InvalidPrefixes() throws IOException {
- update("refs/heads/A", A);
-
- assertTrue("empty refs/heads", refdb.getRefs("refs/heads").isEmpty());
- assertTrue("empty objects", refdb.getRefs("objects").isEmpty());
- assertTrue("empty objects/", refdb.getRefs("objects/").isEmpty());
- }
-
- @Test
- public void testGetRefs_DiscoversNew() throws IOException {
- update("refs/heads/master", A);
- Map<String, Ref> orig = refdb.getRefs(ALL);
-
- update("refs/heads/next", B);
- Map<String, Ref> next = refdb.getRefs(ALL);
-
- assertEquals(1, orig.size());
- assertEquals(2, next.size());
-
- assertFalse(orig.containsKey("refs/heads/next"));
- assertTrue(next.containsKey("refs/heads/next"));
-
- assertEquals(A, next.get("refs/heads/master").getObjectId());
- assertEquals(B, next.get("refs/heads/next").getObjectId());
- }
-
- @Test
- public void testGetRefs_DiscoversModified() throws IOException {
- symref(HEAD, "refs/heads/master");
- update("refs/heads/master", A);
-
- Map<String, Ref> all = refdb.getRefs(ALL);
- assertEquals(A, all.get(HEAD).getObjectId());
-
- update("refs/heads/master", B);
- all = refdb.getRefs(ALL);
- assertEquals(B, all.get(HEAD).getObjectId());
- assertEquals(B, refdb.exactRef(HEAD).getObjectId());
- }
-
- @Test
- public void testGetRefs_CycleInSymbolicRef() throws IOException {
- symref("refs/1", "refs/2");
- symref("refs/2", "refs/3");
- symref("refs/3", "refs/4");
- symref("refs/4", "refs/5");
- symref("refs/5", "refs/end");
- update("refs/end", A);
-
- Map<String, Ref> all = refdb.getRefs(ALL);
- Ref r = all.get("refs/1");
- assertNotNull("has 1", r);
-
- assertEquals("refs/1", r.getName());
- assertEquals(A, r.getObjectId());
- assertTrue(r.isSymbolic());
-
- r = r.getTarget();
- assertEquals("refs/2", r.getName());
- assertEquals(A, r.getObjectId());
- assertTrue(r.isSymbolic());
-
- r = r.getTarget();
- assertEquals("refs/3", r.getName());
- assertEquals(A, r.getObjectId());
- assertTrue(r.isSymbolic());
-
- r = r.getTarget();
- assertEquals("refs/4", r.getName());
- assertEquals(A, r.getObjectId());
- assertTrue(r.isSymbolic());
-
- r = r.getTarget();
- assertEquals("refs/5", r.getName());
- assertEquals(A, r.getObjectId());
- assertTrue(r.isSymbolic());
-
- r = r.getTarget();
- assertEquals("refs/end", r.getName());
- assertEquals(A, r.getObjectId());
- assertFalse(r.isSymbolic());
-
- symref("refs/5", "refs/6");
- symref("refs/6", "refs/end");
- all = refdb.getRefs(ALL);
- assertNull("mising 1 due to cycle", all.get("refs/1"));
- assertEquals(A, all.get("refs/2").getObjectId());
- assertEquals(A, all.get("refs/3").getObjectId());
- assertEquals(A, all.get("refs/4").getObjectId());
- assertEquals(A, all.get("refs/5").getObjectId());
- assertEquals(A, all.get("refs/6").getObjectId());
- assertEquals(A, all.get("refs/end").getObjectId());
- }
-
- @Test
- public void testGetRef_NonExistingBranchConfig() throws IOException {
- assertNull("find branch config", refdb.findRef("config"));
- assertNull("find branch config", refdb.findRef("refs/heads/config"));
- }
-
- @Test
- public void testGetRef_FindBranchConfig() throws IOException {
- update("refs/heads/config", A);
-
- for (String t : new String[] { "config", "refs/heads/config" }) {
- Ref r = refdb.findRef(t);
- assertNotNull("find branch config (" + t + ")", r);
- assertEquals("for " + t, "refs/heads/config", r.getName());
- assertEquals("for " + t, A, r.getObjectId());
- }
- }
-
- @Test
- public void testFirstExactRef() throws IOException {
- update("refs/heads/A", A);
- update("refs/tags/v1.0", v1_0);
-
- Ref a = refdb.firstExactRef("refs/heads/A", "refs/tags/v1.0");
- Ref one = refdb.firstExactRef("refs/tags/v1.0", "refs/heads/A");
-
- assertEquals("refs/heads/A", a.getName());
- assertEquals("refs/tags/v1.0", one.getName());
-
- assertEquals(A, a.getObjectId());
- assertEquals(v1_0, one.getObjectId());
- }
-
- @Test
- public void testExactRef_DiscoversModified() throws IOException {
- symref(HEAD, "refs/heads/master");
- update("refs/heads/master", A);
- assertEquals(A, refdb.exactRef(HEAD).getObjectId());
-
- update("refs/heads/master", B);
- assertEquals(B, refdb.exactRef(HEAD).getObjectId());
- }
-
- @Test
- public void testIsNameConflicting() throws IOException {
- update("refs/heads/a/b", A);
- update("refs/heads/q", B);
-
- // new references cannot replace an existing container
- assertTrue(refdb.isNameConflicting("refs"));
- assertTrue(refdb.isNameConflicting("refs/heads"));
- assertTrue(refdb.isNameConflicting("refs/heads/a"));
-
- // existing reference is not conflicting
- assertFalse(refdb.isNameConflicting("refs/heads/a/b"));
-
- // new references are not conflicting
- assertFalse(refdb.isNameConflicting("refs/heads/a/d"));
- assertFalse(refdb.isNameConflicting("refs/heads/master"));
-
- // existing reference must not be used as a container
- assertTrue(refdb.isNameConflicting("refs/heads/a/b/c"));
- assertTrue(refdb.isNameConflicting("refs/heads/q/master"));
-
- // refs/txn/ names always conflict.
- assertTrue(refdb.isNameConflicting(refdb.getTxnCommitted()));
- assertTrue(refdb.isNameConflicting("refs/txn/foo"));
- }
-
- @Test
- public void testUpdate_RefusesRefsTxnNamespace() throws IOException {
- ObjectId txnId = getTxnCommitted();
-
- RefUpdate u = refdb.newUpdate("refs/txn/tmp", false);
- u.setNewObjectId(B);
- assertEquals(RefUpdate.Result.LOCK_FAILURE, u.update());
- assertEquals(txnId, getTxnCommitted());
-
- ReceiveCommand cmd = command(null, B, "refs/txn/tmp");
- BatchRefUpdate batch = refdb.newBatchUpdate();
- batch.addCommand(cmd);
- try (RevWalk rw = new RevWalk(repo)) {
- batch.execute(rw, NullProgressMonitor.INSTANCE);
- }
- assertEquals(REJECTED_OTHER_REASON, cmd.getResult());
- assertEquals(MessageFormat.format(JGitText.get().invalidRefName,
- "refs/txn/tmp"), cmd.getMessage());
- assertEquals(txnId, getTxnCommitted());
- }
-
- @Test
- public void testUpdate_RefusesDotLockInRefName() throws IOException {
- ObjectId txnId = getTxnCommitted();
-
- RefUpdate u = refdb.newUpdate("refs/heads/pu.lock", false);
- u.setNewObjectId(B);
- assertEquals(RefUpdate.Result.REJECTED, u.update());
- assertEquals(txnId, getTxnCommitted());
-
- ReceiveCommand cmd = command(null, B, "refs/heads/pu.lock");
- BatchRefUpdate batch = refdb.newBatchUpdate();
- batch.addCommand(cmd);
- try (RevWalk rw = new RevWalk(repo)) {
- batch.execute(rw, NullProgressMonitor.INSTANCE);
- }
- assertEquals(REJECTED_OTHER_REASON, cmd.getResult());
- assertEquals(JGitText.get().funnyRefname, cmd.getMessage());
- assertEquals(txnId, getTxnCommitted());
- }
-
- @Test
- public void testUpdate_RefusesOrigHeadOnBare() throws IOException {
- assertTrue(refdb.getRepository().isBare());
- ObjectId txnId = getTxnCommitted();
-
- RefUpdate orig = refdb.newUpdate(ORIG_HEAD, true);
- orig.setNewObjectId(B);
- assertEquals(RefUpdate.Result.LOCK_FAILURE, orig.update());
- assertEquals(txnId, getTxnCommitted());
-
- ReceiveCommand cmd = command(null, B, ORIG_HEAD);
- BatchRefUpdate batch = refdb.newBatchUpdate();
- batch.addCommand(cmd);
- try (RevWalk rw = new RevWalk(repo)) {
- batch.execute(rw, NullProgressMonitor.INSTANCE);
- }
- assertEquals(REJECTED_OTHER_REASON, cmd.getResult());
- assertEquals(
- MessageFormat.format(JGitText.get().invalidRefName, ORIG_HEAD),
- cmd.getMessage());
- assertEquals(txnId, getTxnCommitted());
- }
-
- @Test
- public void testBatchRefUpdate_NonFastForwardAborts() throws IOException {
- update("refs/heads/master", A);
- update("refs/heads/masters", B);
- ObjectId txnId = getTxnCommitted();
-
- List<ReceiveCommand> commands = Arrays.asList(
- command(A, B, "refs/heads/master"),
- command(B, A, "refs/heads/masters"));
- BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
- batchUpdate.addCommand(commands);
- try (RevWalk rw = new RevWalk(repo)) {
- batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
- }
- assertEquals(txnId, getTxnCommitted());
-
- assertEquals(REJECTED_NONFASTFORWARD,
- commands.get(1).getResult());
- assertEquals(REJECTED_OTHER_REASON,
- commands.get(0).getResult());
- assertEquals(JGitText.get().transactionAborted,
- commands.get(0).getMessage());
- }
-
- @Test
- public void testBatchRefUpdate_ForceUpdate() throws IOException {
- update("refs/heads/master", A);
- update("refs/heads/masters", B);
- ObjectId txnId = getTxnCommitted();
-
- List<ReceiveCommand> commands = Arrays.asList(
- command(A, B, "refs/heads/master"),
- command(B, A, "refs/heads/masters"));
- BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
- batchUpdate.setAllowNonFastForwards(true);
- batchUpdate.addCommand(commands);
- try (RevWalk rw = new RevWalk(repo)) {
- batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
- }
- assertNotEquals(txnId, getTxnCommitted());
-
- Map<String, Ref> refs = refdb.getRefs(ALL);
- assertEquals(OK, commands.get(0).getResult());
- assertEquals(OK, commands.get(1).getResult());
- assertEquals(
- "[refs/heads/master, refs/heads/masters]",
- refs.keySet().toString());
- assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId());
- assertEquals(A.getId(), refs.get("refs/heads/masters").getObjectId());
- }
-
- @Test
- public void testBatchRefUpdate_NonFastForwardDoesNotDoExpensiveMergeCheck()
- throws IOException {
- update("refs/heads/master", B);
- ObjectId txnId = getTxnCommitted();
-
- List<ReceiveCommand> commands = Arrays.asList(
- command(B, A, "refs/heads/master"));
- BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
- batchUpdate.setAllowNonFastForwards(true);
- batchUpdate.addCommand(commands);
- try (RevWalk rw = new RevWalk(repo) {
- @Override
- public boolean isMergedInto(RevCommit base, RevCommit tip) {
- fail("isMergedInto() should not be called");
- return false;
- }
- }) {
- batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
- }
- assertNotEquals(txnId, getTxnCommitted());
-
- Map<String, Ref> refs = refdb.getRefs(ALL);
- assertEquals(OK, commands.get(0).getResult());
- assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId());
- }
-
- @Test
- public void testBatchRefUpdate_ConflictCausesAbort() throws IOException {
- update("refs/heads/master", A);
- update("refs/heads/masters", B);
- ObjectId txnId = getTxnCommitted();
-
- List<ReceiveCommand> commands = Arrays.asList(
- command(A, B, "refs/heads/master"),
- command(null, A, "refs/heads/master/x"),
- command(null, A, "refs/heads"));
- BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
- batchUpdate.setAllowNonFastForwards(true);
- batchUpdate.addCommand(commands);
- try (RevWalk rw = new RevWalk(repo)) {
- batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
- }
- assertEquals(txnId, getTxnCommitted());
-
- assertEquals(LOCK_FAILURE, commands.get(0).getResult());
-
- assertEquals(REJECTED_OTHER_REASON, commands.get(1).getResult());
- assertEquals(JGitText.get().transactionAborted,
- commands.get(1).getMessage());
-
- assertEquals(REJECTED_OTHER_REASON, commands.get(2).getResult());
- assertEquals(JGitText.get().transactionAborted,
- commands.get(2).getMessage());
- }
-
- @Test
- public void testBatchRefUpdate_NoConflictIfDeleted() throws IOException {
- update("refs/heads/master", A);
- update("refs/heads/masters", B);
- ObjectId txnId = getTxnCommitted();
-
- List<ReceiveCommand> commands = Arrays.asList(
- command(A, B, "refs/heads/master"),
- command(null, A, "refs/heads/masters/x"),
- command(B, null, "refs/heads/masters"));
- BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
- batchUpdate.setAllowNonFastForwards(true);
- batchUpdate.addCommand(commands);
- try (RevWalk rw = new RevWalk(repo)) {
- batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
- }
- assertNotEquals(txnId, getTxnCommitted());
-
- assertEquals(OK, commands.get(0).getResult());
- assertEquals(OK, commands.get(1).getResult());
- assertEquals(OK, commands.get(2).getResult());
-
- Map<String, Ref> refs = refdb.getRefs(ALL);
- assertEquals(
- "[refs/heads/master, refs/heads/masters/x]",
- refs.keySet().toString());
- assertEquals(A.getId(), refs.get("refs/heads/masters/x").getObjectId());
- }
-
- private ObjectId getTxnCommitted() throws IOException {
- Ref r = bootstrap.exactRef(refdb.getTxnCommitted());
- if (r != null && r.getObjectId() != null) {
- return r.getObjectId();
- }
- return ObjectId.zeroId();
- }
-
- private static ReceiveCommand command(AnyObjectId a, AnyObjectId b,
- String name) {
- return new ReceiveCommand(
- a != null ? a.copy() : ObjectId.zeroId(),
- b != null ? b.copy() : ObjectId.zeroId(),
- name);
- }
-
- private void symref(String name, String dst)
- throws IOException {
- commit((ObjectReader reader, RefTree tree) -> {
- Ref old = tree.exactRef(reader, name);
- Command n = new Command(old, new SymbolicRef(name,
- new ObjectIdRef.Unpeeled(Ref.Storage.NEW, dst, null)));
- return tree.apply(Collections.singleton(n));
- });
- }
-
- private void update(String name, ObjectId id)
- throws IOException {
- commit((ObjectReader reader, RefTree tree) -> {
- Ref old = tree.exactRef(reader, name);
- Command n;
- try (RevWalk rw = new RevWalk(repo)) {
- n = new Command(old, Command.toRef(rw, id, null, name, true));
- }
- return tree.apply(Collections.singleton(n));
- });
- }
-
- interface Function {
- boolean apply(ObjectReader reader, RefTree tree) throws IOException;
- }
-
- private void commit(Function fun) throws IOException {
- try (ObjectReader reader = repo.newObjectReader();
- ObjectInserter inserter = repo.newObjectInserter();
- RevWalk rw = new RevWalk(reader)) {
- RefUpdate u = bootstrap.newUpdate(refdb.getTxnCommitted(), false);
- CommitBuilder cb = new CommitBuilder();
- testRepo.setAuthorAndCommitter(cb);
-
- Ref ref = bootstrap.exactRef(refdb.getTxnCommitted());
- RefTree tree;
- if (ref != null && ref.getObjectId() != null) {
- tree = RefTree.read(reader, rw.parseTree(ref.getObjectId()));
- cb.setParentId(ref.getObjectId());
- u.setExpectedOldObjectId(ref.getObjectId());
- } else {
- tree = RefTree.newEmptyTree();
- u.setExpectedOldObjectId(ObjectId.zeroId());
- }
-
- assertTrue(fun.apply(reader, tree));
- cb.setTreeId(tree.writeTree(inserter));
- u.setNewObjectId(inserter.insert(cb));
- inserter.flush();
- switch (u.update(rw)) {
- case NEW:
- case FAST_FORWARD:
- break;
- default:
- fail("Expected " + u.getName() + " to update");
- }
- }
- }
-
- private class InMemRefTreeRepo extends InMemoryRepository {
- private final RefTreeDatabase refs;
-
- InMemRefTreeRepo(DfsRepositoryDescription repoDesc) {
- super(repoDesc);
- refs = new RefTreeDatabase(this, super.getRefDatabase(),
- "refs/txn/committed");
- RefTreeDatabaseTest.this.refdb = refs;
- }
-
- @Override
- public RefDatabase getRefDatabase() {
- return refs;
- }
- }
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeTest.java
deleted file mode 100644
index a5b01900cd..0000000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeTest.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.storage.reftree;
-
-import static org.eclipse.jgit.lib.Constants.HEAD;
-import static org.eclipse.jgit.lib.Constants.R_HEADS;
-import static org.eclipse.jgit.lib.Constants.R_TAGS;
-import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
-import static org.eclipse.jgit.lib.Ref.Storage.NEW;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.LOCK_FAILURE;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
-import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
-import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.SymbolicRef;
-import org.eclipse.jgit.revwalk.RevBlob;
-import org.eclipse.jgit.revwalk.RevTag;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.junit.Before;
-import org.junit.Test;
-
-public class RefTreeTest {
- private static final String R_MASTER = R_HEADS + "master";
- private InMemoryRepository repo;
- private TestRepository<InMemoryRepository> git;
-
- @Before
- public void setUp() throws IOException {
- repo = new InMemoryRepository(new DfsRepositoryDescription("RefTree"));
- git = new TestRepository<>(repo);
- }
-
- @Test
- public void testEmptyTree() throws IOException {
- RefTree tree = RefTree.newEmptyTree();
- try (ObjectReader reader = repo.newObjectReader()) {
- assertNull(HEAD, tree.exactRef(reader, HEAD));
- assertNull("master", tree.exactRef(reader, R_MASTER));
- }
- }
-
- @Test
- public void testApplyThenReadMaster() throws Exception {
- RefTree tree = RefTree.newEmptyTree();
- RevBlob id = git.blob("A");
- Command cmd = new Command(null, ref(R_MASTER, id));
- assertTrue(tree.apply(Collections.singletonList(cmd)));
- assertSame(NOT_ATTEMPTED, cmd.getResult());
-
- try (ObjectReader reader = repo.newObjectReader()) {
- Ref m = tree.exactRef(reader, R_MASTER);
- assertNotNull(R_MASTER, m);
- assertEquals(R_MASTER, m.getName());
- assertEquals(id, m.getObjectId());
- assertTrue("peeled", m.isPeeled());
- }
- }
-
- @Test
- public void testUpdateMaster() throws Exception {
- RefTree tree = RefTree.newEmptyTree();
- RevBlob id1 = git.blob("A");
- Command cmd1 = new Command(null, ref(R_MASTER, id1));
- assertTrue(tree.apply(Collections.singletonList(cmd1)));
- assertSame(NOT_ATTEMPTED, cmd1.getResult());
-
- RevBlob id2 = git.blob("B");
- Command cmd2 = new Command(ref(R_MASTER, id1), ref(R_MASTER, id2));
- assertTrue(tree.apply(Collections.singletonList(cmd2)));
- assertSame(NOT_ATTEMPTED, cmd2.getResult());
-
- try (ObjectReader reader = repo.newObjectReader()) {
- Ref m = tree.exactRef(reader, R_MASTER);
- assertNotNull(R_MASTER, m);
- assertEquals(R_MASTER, m.getName());
- assertEquals(id2, m.getObjectId());
- assertTrue("peeled", m.isPeeled());
- }
- }
-
- @Test
- public void testHeadSymref() throws Exception {
- RefTree tree = RefTree.newEmptyTree();
- RevBlob id = git.blob("A");
- Command cmd1 = new Command(null, ref(R_MASTER, id));
- Command cmd2 = new Command(null, symref(HEAD, R_MASTER));
- assertTrue(tree.apply(Arrays.asList(new Command[] { cmd1, cmd2 })));
- assertSame(NOT_ATTEMPTED, cmd1.getResult());
- assertSame(NOT_ATTEMPTED, cmd2.getResult());
-
- try (ObjectReader reader = repo.newObjectReader()) {
- Ref m = tree.exactRef(reader, HEAD);
- assertNotNull(HEAD, m);
- assertEquals(HEAD, m.getName());
- assertTrue("symbolic", m.isSymbolic());
- assertNotNull(m.getTarget());
- assertEquals(R_MASTER, m.getTarget().getName());
- assertEquals(id, m.getTarget().getObjectId());
- }
-
- // Writing flushes some buffers, re-read from blob.
- ObjectId newId = write(tree);
- try (ObjectReader reader = repo.newObjectReader();
- RevWalk rw = new RevWalk(reader)) {
- tree = RefTree.read(reader, rw.parseTree(newId));
- Ref m = tree.exactRef(reader, HEAD);
- assertEquals(R_MASTER, m.getTarget().getName());
- }
- }
-
- @Test
- public void testTagIsPeeled() throws Exception {
- String name = "v1.0";
- RefTree tree = RefTree.newEmptyTree();
- RevBlob id = git.blob("A");
- RevTag tag = git.tag(name, id);
-
- String ref = R_TAGS + name;
- Command cmd = create(ref, tag);
- assertTrue(tree.apply(Collections.singletonList(cmd)));
- assertSame(NOT_ATTEMPTED, cmd.getResult());
-
- try (ObjectReader reader = repo.newObjectReader()) {
- Ref m = tree.exactRef(reader, ref);
- assertNotNull(ref, m);
- assertEquals(ref, m.getName());
- assertEquals(tag, m.getObjectId());
- assertTrue("peeled", m.isPeeled());
- assertEquals(id, m.getPeeledObjectId());
- }
- }
-
- @Test
- public void testApplyAlreadyExists() throws Exception {
- RefTree tree = RefTree.newEmptyTree();
- RevBlob a = git.blob("A");
- Command cmd = new Command(null, ref(R_MASTER, a));
- assertTrue(tree.apply(Collections.singletonList(cmd)));
- ObjectId treeId = write(tree);
-
- RevBlob b = git.blob("B");
- Command cmd1 = create(R_MASTER, b);
- Command cmd2 = create(R_MASTER, b);
- assertFalse(tree.apply(Arrays.asList(new Command[] { cmd1, cmd2 })));
- assertSame(LOCK_FAILURE, cmd1.getResult());
- assertSame(REJECTED_OTHER_REASON, cmd2.getResult());
- assertEquals(JGitText.get().transactionAborted, cmd2.getMessage());
- assertEquals(treeId, write(tree));
- }
-
- @Test
- public void testApplyWrongOldId() throws Exception {
- RefTree tree = RefTree.newEmptyTree();
- RevBlob a = git.blob("A");
- Command cmd = new Command(null, ref(R_MASTER, a));
- assertTrue(tree.apply(Collections.singletonList(cmd)));
- ObjectId treeId = write(tree);
-
- RevBlob b = git.blob("B");
- RevBlob c = git.blob("C");
- Command cmd1 = update(R_MASTER, b, c);
- Command cmd2 = create(R_MASTER, b);
- assertFalse(tree.apply(Arrays.asList(new Command[] { cmd1, cmd2 })));
- assertSame(LOCK_FAILURE, cmd1.getResult());
- assertSame(REJECTED_OTHER_REASON, cmd2.getResult());
- assertEquals(JGitText.get().transactionAborted, cmd2.getMessage());
- assertEquals(treeId, write(tree));
- }
-
- @Test
- public void testApplyWrongOldIdButAlreadyCurrentIsNoOp() throws Exception {
- RefTree tree = RefTree.newEmptyTree();
- RevBlob a = git.blob("A");
- Command cmd = new Command(null, ref(R_MASTER, a));
- assertTrue(tree.apply(Collections.singletonList(cmd)));
- ObjectId treeId = write(tree);
-
- RevBlob b = git.blob("B");
- cmd = update(R_MASTER, b, a);
- assertTrue(tree.apply(Collections.singletonList(cmd)));
- assertEquals(treeId, write(tree));
- }
-
- @Test
- public void testApplyCannotCreateSubdirectory() throws Exception {
- RefTree tree = RefTree.newEmptyTree();
- RevBlob a = git.blob("A");
- Command cmd = new Command(null, ref(R_MASTER, a));
- assertTrue(tree.apply(Collections.singletonList(cmd)));
- ObjectId treeId = write(tree);
-
- RevBlob b = git.blob("B");
- Command cmd1 = create(R_MASTER + "/fail", b);
- assertFalse(tree.apply(Collections.singletonList(cmd1)));
- assertSame(LOCK_FAILURE, cmd1.getResult());
- assertEquals(treeId, write(tree));
- }
-
- @Test
- public void testApplyCannotCreateParentRef() throws Exception {
- RefTree tree = RefTree.newEmptyTree();
- RevBlob a = git.blob("A");
- Command cmd = new Command(null, ref(R_MASTER, a));
- assertTrue(tree.apply(Collections.singletonList(cmd)));
- ObjectId treeId = write(tree);
-
- RevBlob b = git.blob("B");
- Command cmd1 = create("refs/heads", b);
- assertFalse(tree.apply(Collections.singletonList(cmd1)));
- assertSame(LOCK_FAILURE, cmd1.getResult());
- assertEquals(treeId, write(tree));
- }
-
- private static Ref ref(String name, ObjectId id) {
- return new ObjectIdRef.PeeledNonTag(LOOSE, name, id);
- }
-
- private static Ref symref(String name, String dest) {
- Ref d = new ObjectIdRef.PeeledNonTag(NEW, dest, null);
- return new SymbolicRef(name, d);
- }
-
- private Command create(String name, ObjectId id)
- throws MissingObjectException, IOException {
- return update(name, ObjectId.zeroId(), id);
- }
-
- private Command update(String name, ObjectId oldId, ObjectId newId)
- throws MissingObjectException, IOException {
- try (RevWalk rw = new RevWalk(repo)) {
- return new Command(rw, new ReceiveCommand(oldId, newId, name));
- }
- }
-
- private ObjectId write(RefTree tree) throws IOException {
- try (ObjectInserter ins = repo.newObjectInserter()) {
- ObjectId id = tree.writeTree(ins);
- ins.flush();
- return id;
- }
- }
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitBuilderTest.java
index dee58f9cfc..2f1bada82a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitBuilderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitBuilderTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, Salesforce. and others
+ * Copyright (C) 2018, 2020 Salesforce. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -53,7 +53,7 @@ public class CommitBuilderTest {
private void assertGpgSignatureStringOutcome(String signature,
String expectedOutcome) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
- CommitBuilder.writeGpgSignatureString(signature, out);
+ ObjectBuilder.writeMultiLineHeader(signature, out, true);
String formatted_signature = new String(out.toByteArray(), US_ASCII);
assertEquals(expectedOutcome, formatted_signature);
}
@@ -85,8 +85,8 @@ public class CommitBuilderTest {
String signature = "Ü Ä";
IllegalArgumentException e = assertThrows(
IllegalArgumentException.class,
- () -> CommitBuilder.writeGpgSignatureString(signature,
- new ByteArrayOutputStream()));
+ () -> ObjectBuilder.writeMultiLineHeader(signature,
+ new ByteArrayOutputStream(), true));
String message = MessageFormat.format(JGitText.get().notASCIIString,
signature);
assertEquals(message, e.getMessage());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
index 88d17ec153..7590048a71 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
@@ -26,6 +26,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -318,6 +319,64 @@ public class RefTest extends SampleDataRepositoryTestCase {
}
@Test
+ public void testGetRefsExcludingPrefix() throws IOException {
+ Set<String> exclude = new HashSet<>();
+ exclude.add("refs/tags");
+ // HEAD + 12 refs/heads are present here.
+ List<Ref> refs =
+ db.getRefDatabase().getRefsByPrefixWithExclusions(RefDatabase.ALL, exclude);
+ assertEquals(13, refs.size());
+ checkContainsRef(refs, db.exactRef("HEAD"));
+ checkContainsRef(refs, db.exactRef("refs/heads/a"));
+ for (Ref notInResult : db.getRefDatabase().getRefsByPrefix("refs/tags")) {
+ assertFalse(refs.contains(notInResult));
+ }
+ }
+
+ @Test
+ public void testGetRefsExcludingPrefixes() throws IOException {
+ Set<String> exclude = new HashSet<>();
+ exclude.add("refs/tags/");
+ exclude.add("refs/heads/");
+ List<Ref> refs = db.getRefDatabase().getRefsByPrefixWithExclusions(RefDatabase.ALL, exclude);
+ assertEquals(1, refs.size());
+ checkContainsRef(refs, db.exactRef("HEAD"));
+ }
+
+ @Test
+ public void testGetRefsExcludingNonExistingPrefixes() throws IOException {
+ Set<String> prefixes = new HashSet<>();
+ prefixes.add("refs/tags/");
+ prefixes.add("refs/heads/");
+ prefixes.add("refs/nonexistent/");
+ List<Ref> refs = db.getRefDatabase().getRefsByPrefixWithExclusions(RefDatabase.ALL, prefixes);
+ assertEquals(1, refs.size());
+ checkContainsRef(refs, db.exactRef("HEAD"));
+ }
+
+ @Test
+ public void testGetRefsWithPrefixExcludingPrefixes() throws IOException {
+ Set<String> exclude = new HashSet<>();
+ exclude.add("refs/heads/pa");
+ String include = "refs/heads/p";
+ List<Ref> refs = db.getRefDatabase().getRefsByPrefixWithExclusions(include, exclude);
+ assertEquals(1, refs.size());
+ checkContainsRef(refs, db.exactRef("refs/heads/prefix/a"));
+ }
+
+ @Test
+ public void testGetRefsWithPrefixExcludingOverlappingPrefixes() throws IOException {
+ Set<String> exclude = new HashSet<>();
+ exclude.add("refs/heads/pa");
+ exclude.add("refs/heads/");
+ exclude.add("refs/heads/p");
+ exclude.add("refs/tags/");
+ List<Ref> refs = db.getRefDatabase().getRefsByPrefixWithExclusions(RefDatabase.ALL, exclude);
+ assertEquals(1, refs.size());
+ checkContainsRef(refs, db.exactRef("HEAD"));
+ }
+
+ @Test
public void testResolveTipSha1() throws IOException {
ObjectId masterId = db.resolve("refs/heads/master");
Set<Ref> resolved = db.getRefDatabase().getTipsWithSha1(masterId);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TagBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TagBuilderTest.java
new file mode 100644
index 0000000000..578602224c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TagBuilderTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.junit.Test;
+
+public class TagBuilderTest {
+
+ // @formatter:off
+ private static final String SIGNATURE = "-----BEGIN PGP SIGNATURE-----\n" +
+ "Version: BCPG v1.60\n" +
+ "\n" +
+ "iQEcBAABCAAGBQJb9cVhAAoJEKX+6Axg/6TZeFsH/0CY0WX/z7U8+7S5giFX4wH4\n" +
+ "opvBwqyt6OX8lgNwTwBGHFNt8LdmDCCmKoq/XwkNi3ARVjLhe3gBcKXNoavvPk2Z\n" +
+ "gIg5ChevGkU4afWCOMLVEYnkCBGw2+86XhrK1P7gTHEk1Rd+Yv1ZRDJBY+fFO7yz\n" +
+ "uSBuF5RpEY2sJiIvp27Gub/rY3B5NTR/feO/z+b9oiP/fMUhpRwG5KuWUsn9NPjw\n" +
+ "3tvbgawYpU/2UnS+xnavMY4t2fjRYjsoxndPLb2MUX8X7vC7FgWLBlmI/rquLZVM\n" +
+ "IQEKkjnA+lhejjK1rv+ulq4kGZJFKGYWYYhRDwFg5PTkzhudhN2SGUq5Wxq1Eg4=\n" +
+ "=b9OI\n" +
+ "-----END PGP SIGNATURE-----";
+
+ // @formatter:on
+
+ private static final String TAGGER_LINE = "A U. Thor <a_u_thor@example.com> 1218123387 +0700";
+
+ private static final PersonIdent TAGGER = RawParseUtils
+ .parsePersonIdent(TAGGER_LINE);
+
+ @Test
+ public void testTagSimple() throws Exception {
+ TagBuilder t = new TagBuilder();
+ t.setTag("sometag");
+ t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+ t.setEncoding(US_ASCII);
+ t.setMessage("Short message only");
+ t.setTagger(TAGGER);
+ String tag = new String(t.build(), UTF_8);
+ String expected = "object 0000000000000000000000000000000000000000\n"
+ + "type commit\n" //
+ + "tag sometag\n" //
+ + "tagger " + TAGGER_LINE + '\n' //
+ + "encoding US-ASCII\n" //
+ + '\n' //
+ + "Short message only";
+ assertEquals(expected, tag);
+ }
+
+ @Test
+ public void testTagWithSignatureShortMessageEndsInLF() throws Exception {
+ TagBuilder t = new TagBuilder();
+ t.setTag("sometag");
+ t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+ t.setEncoding(US_ASCII);
+ t.setMessage("Short message only\n");
+ t.setTagger(TAGGER);
+ t.setGpgSignature(new GpgSignature(SIGNATURE.getBytes(US_ASCII)));
+ String tag = new String(t.build(), UTF_8);
+ String expected = "object 0000000000000000000000000000000000000000\n"
+ + "type commit\n" //
+ + "tag sometag\n" //
+ + "tagger " + TAGGER_LINE + '\n' //
+ + "encoding US-ASCII\n" //
+ + '\n' //
+ + "Short message only\n" //
+ + SIGNATURE + '\n';
+ assertEquals(expected, tag);
+ }
+
+ @Test
+ public void testTagWithSignatureMessageNoLF() {
+ TagBuilder t = new TagBuilder();
+ t.setTag("sometag");
+ t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+ t.setEncoding(US_ASCII);
+ t.setMessage("A message\n\nthat does not end in LF");
+ t.setTagger(TAGGER);
+ t.setGpgSignature(new GpgSignature(SIGNATURE.getBytes(US_ASCII)));
+ Throwable ex = assertThrows(Throwable.class, t::build);
+ assertEquals(JGitText.get().signedTagMessageNoLf, ex.getMessage());
+ }
+
+ @Test
+ public void testTagWithSignatureNoParagraphsMessage() throws Exception {
+ TagBuilder t = new TagBuilder();
+ t.setTag("sometag");
+ t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+ t.setEncoding(US_ASCII);
+ t.setMessage("A strange\ntag message\n");
+ t.setTagger(TAGGER);
+ t.setGpgSignature(new GpgSignature(SIGNATURE.getBytes(US_ASCII)));
+ String tag = new String(t.build(), UTF_8);
+ String expected = "object 0000000000000000000000000000000000000000\n"
+ + "type commit\n" //
+ + "tag sometag\n" //
+ + "tagger " + TAGGER_LINE + '\n' //
+ + "encoding US-ASCII\n" //
+ + '\n' //
+ + "A strange\ntag message\n" //
+ + SIGNATURE + '\n';
+ assertEquals(expected, tag);
+ }
+
+ @Test
+ public void testTagWithSignatureLongMessage() throws Exception {
+ TagBuilder t = new TagBuilder();
+ t.setTag("sometag");
+ t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+ t.setMessage("Short message\n\nFollowed by explanations.\n");
+ t.setTagger(TAGGER);
+ t.setGpgSignature(new GpgSignature(SIGNATURE.getBytes(US_ASCII)));
+ String tag = new String(t.build(), UTF_8);
+ String expected = "object 0000000000000000000000000000000000000000\n"
+ + "type commit\n" //
+ + "tag sometag\n" //
+ + "tagger " + TAGGER_LINE + '\n' //
+ + '\n' //
+ + "Short message\n\nFollowed by explanations.\n" //
+ + SIGNATURE + '\n';
+ assertEquals(expected, tag);
+ }
+
+ @Test
+ public void testTagWithSignatureEmptyMessage() throws Exception {
+ TagBuilder t = new TagBuilder();
+ t.setTag("sometag");
+ t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+ t.setTagger(TAGGER);
+ t.setMessage("");
+ String emptyMsg = new String(t.build(), UTF_8);
+ t.setGpgSignature(new GpgSignature(SIGNATURE.getBytes(US_ASCII)));
+ String tag = new String(t.build(), UTF_8);
+ String expected = "object 0000000000000000000000000000000000000000\n"
+ + "type commit\n" //
+ + "tag sometag\n" //
+ + "tagger " + TAGGER_LINE + '\n' //
+ + '\n';
+ assertEquals(expected, emptyMsg);
+ assertEquals(expected + SIGNATURE + '\n', tag);
+ }
+
+ @Test
+ public void testTagWithSignatureOnly() throws Exception {
+ TagBuilder t = new TagBuilder();
+ t.setTag("sometag");
+ t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+ t.setTagger(TAGGER);
+ String emptyMsg = new String(t.build(), UTF_8);
+ t.setGpgSignature(new GpgSignature(SIGNATURE.getBytes(US_ASCII)));
+ String tag = new String(t.build(), UTF_8);
+ String expected = "object 0000000000000000000000000000000000000000\n"
+ + "type commit\n" //
+ + "tag sometag\n" //
+ + "tagger " + TAGGER_LINE + '\n' //
+ + '\n';
+ assertEquals(expected, emptyMsg);
+ assertEquals(expected + SIGNATURE + '\n', tag);
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
index e2ac89be90..eecf25be90 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
@@ -1384,6 +1384,270 @@ public class MergerTest extends RepositoryTestCase {
git.merge().include(commitB).call();
}
+ /**
+ * Merging two commits with a file/dir conflict in the virtual ancestor.
+ *
+ * <p>
+ * Those conflicts should be ignored, otherwise the found base can not be used by the
+ * RecursiveMerger.
+ * <pre>
+ * --------------
+ * | \
+ * | C1 - C4 --- ? master
+ * | / /
+ * | I - A1 - C2 - C3 second-branch
+ * | \ /
+ * \ \ /
+ * ----A2-------- branch-to-merge
+ * </pre>
+ * <p>
+ * <p>
+ * Path "a" is initially a file in I and A1. It is changed to a directory in A2
+ * ("branch-to-merge").
+ * <p>
+ * A2 is merged into "master" and "second-branch". The dir/file merge conflict is resolved
+ * manually, results in C4 and C3.
+ * <p>
+ * While merging C3 and C4, A1 and A2 are the base commits found by the recursive merge that
+ * have the dir/file conflict.
+ */
+ @Theory
+ public void checkFileDirMergeConflictInVirtualAncestor_NoConflictInChildren(
+ MergeStrategy strategy)
+ throws Exception {
+ if (!strategy.equals(MergeStrategy.RECURSIVE)) {
+ return;
+ }
+
+ Git git = Git.wrap(db);
+
+ // master
+ writeTrashFile("a", "initial content");
+ git.add().addFilepattern("a").call();
+ RevCommit commitI = git.commit().setMessage("Initial commit").call();
+
+ writeTrashFile("a", "content in Ancestor 1");
+ git.add().addFilepattern("a").call();
+ RevCommit commitA1 = git.commit().setMessage("Ancestor 1").call();
+
+ writeTrashFile("a", "content in Child 1 (commited on master)");
+ git.add().addFilepattern("a").call();
+ // commit C1M
+ git.commit().setMessage("Child 1 on master").call();
+
+ git.checkout().setCreateBranch(true).setStartPoint(commitI).setName("branch-to-merge").call();
+ // "a" becomes a directory in A2
+ git.rm().addFilepattern("a").call();
+ writeTrashFile("a/content", "content in Ancestor 2 (commited on branch-to-merge)");
+ git.add().addFilepattern("a/content").call();
+ RevCommit commitA2 = git.commit().setMessage("Ancestor 2").call();
+
+ // second branch
+ git.checkout().setCreateBranch(true).setStartPoint(commitA1).setName("second-branch").call();
+ writeTrashFile("a", "content in Child 2 (commited on second-branch)");
+ git.add().addFilepattern("a").call();
+ // commit C2S
+ git.commit().setMessage("Child 2 on second-branch").call();
+
+ // Merge branch-to-merge into second-branch
+ MergeResult mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+ assertEquals(mergeResult.getNewHead(), null);
+ assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+ // Resolve the conflict manually, merge "a" as a file
+ git.rm().addFilepattern("a").call();
+ git.rm().addFilepattern("a/content").call();
+ writeTrashFile("a", "merge conflict resolution");
+ git.add().addFilepattern("a").call();
+ RevCommit commitC3S = git.commit().setMessage("Child 3 on second bug - resolve merge conflict")
+ .call();
+
+ // Merge branch-to-merge into master
+ git.checkout().setName("master").call();
+ mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+ assertEquals(mergeResult.getNewHead(), null);
+ assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+
+ // Resolve the conflict manually - merge "a" as a file
+ git.rm().addFilepattern("a").call();
+ git.rm().addFilepattern("a/content").call();
+ writeTrashFile("a", "merge conflict resolution");
+ git.add().addFilepattern("a").call();
+ // commit C4M
+ git.commit().setMessage("Child 4 on master - resolve merge conflict").call();
+
+ // Merge C4M (second-branch) into master (C3S)
+ // Conflict in virtual base should be here, but there are no conflicts in
+ // children
+ mergeResult = git.merge().include(commitC3S).call();
+ assertEquals(mergeResult.getMergeStatus(), MergeStatus.MERGED);
+
+ }
+
+ @Theory
+ public void checkFileDirMergeConflictInVirtualAncestor_ConflictInChildren_FileDir(MergeStrategy strategy)
+ throws Exception {
+ if (!strategy.equals(MergeStrategy.RECURSIVE)) {
+ return;
+ }
+
+ Git git = Git.wrap(db);
+
+ // master
+ writeTrashFile("a", "initial content");
+ git.add().addFilepattern("a").call();
+ RevCommit commitI = git.commit().setMessage("Initial commit").call();
+
+ writeTrashFile("a", "content in Ancestor 1");
+ git.add().addFilepattern("a").call();
+ RevCommit commitA1 = git.commit().setMessage("Ancestor 1").call();
+
+ writeTrashFile("a", "content in Child 1 (commited on master)");
+ git.add().addFilepattern("a").call();
+ // commit C1M
+ git.commit().setMessage("Child 1 on master").call();
+
+ git.checkout().setCreateBranch(true).setStartPoint(commitI).setName("branch-to-merge").call();
+
+ // "a" becomes a directory in A2
+ git.rm().addFilepattern("a").call();
+ writeTrashFile("a/content", "content in Ancestor 2 (commited on branch-to-merge)");
+ git.add().addFilepattern("a/content").call();
+ RevCommit commitA2 = git.commit().setMessage("Ancestor 2").call();
+
+ // second branch
+ git.checkout().setCreateBranch(true).setStartPoint(commitA1).setName("second-branch").call();
+ writeTrashFile("a", "content in Child 2 (commited on second-branch)");
+ git.add().addFilepattern("a").call();
+ // commit C2S
+ git.commit().setMessage("Child 2 on second-branch").call();
+
+ // Merge branch-to-merge into second-branch
+ MergeResult mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+ assertEquals(mergeResult.getNewHead(), null);
+ assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+ // Resolve the conflict manually - write a file
+ git.rm().addFilepattern("a").call();
+ git.rm().addFilepattern("a/content").call();
+ writeTrashFile("a",
+ "content in Child 3 (commited on second-branch) - merge conflict resolution");
+ git.add().addFilepattern("a").call();
+ RevCommit commitC3S = git.commit().setMessage("Child 3 on second bug - resolve merge conflict")
+ .call();
+
+ // Merge branch-to-merge into master
+ git.checkout().setName("master").call();
+ mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+ assertEquals(mergeResult.getNewHead(), null);
+ assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+
+ // Resolve the conflict manually - write a file
+ git.rm().addFilepattern("a").call();
+ git.rm().addFilepattern("a/content").call();
+ writeTrashFile("a", "content in Child 4 (commited on master) - merge conflict resolution");
+ git.add().addFilepattern("a").call();
+ // commit C4M
+ git.commit().setMessage("Child 4 on master - resolve merge conflict").call();
+
+ // Merge C4M (second-branch) into master (C3S)
+ // Conflict in virtual base should be here
+ mergeResult = git.merge().include(commitC3S).call();
+ assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+ String expected =
+ "<<<<<<< HEAD\n" + "content in Child 4 (commited on master) - merge conflict resolution\n"
+ + "=======\n"
+ + "content in Child 3 (commited on second-branch) - merge conflict resolution\n"
+ + ">>>>>>> " + commitC3S.name() + "\n";
+ assertEquals(expected, read("a"));
+ // Nothing was populated from the ancestors.
+ assertEquals(
+ "[a, mode:100644, stage:2, content:content in Child 4 (commited on master) - merge conflict resolution][a, mode:100644, stage:3, content:content in Child 3 (commited on second-branch) - merge conflict resolution]",
+ indexState(CONTENT));
+ }
+
+ /**
+ * Same test as above, but "a" is a dir in A1 and a file in A2
+ */
+ @Theory
+ public void checkFileDirMergeConflictInVirtualAncestor_ConflictInChildren_DirFile(MergeStrategy strategy)
+ throws Exception {
+ if (!strategy.equals(MergeStrategy.RECURSIVE)) {
+ return;
+ }
+
+ Git git = Git.wrap(db);
+
+ // master
+ writeTrashFile("a/content", "initial content");
+ git.add().addFilepattern("a/content").call();
+ RevCommit commitI = git.commit().setMessage("Initial commit").call();
+
+ writeTrashFile("a/content", "content in Ancestor 1");
+ git.add().addFilepattern("a/content").call();
+ RevCommit commitA1 = git.commit().setMessage("Ancestor 1").call();
+
+ writeTrashFile("a/content", "content in Child 1 (commited on master)");
+ git.add().addFilepattern("a/content").call();
+ // commit C1M
+ git.commit().setMessage("Child 1 on master").call();
+
+ git.checkout().setCreateBranch(true).setStartPoint(commitI).setName("branch-to-merge").call();
+
+ // "a" becomes a file in A2
+ git.rm().addFilepattern("a/content").call();
+ writeTrashFile("a", "content in Ancestor 2 (commited on branch-to-merge)");
+ git.add().addFilepattern("a").call();
+ RevCommit commitA2 = git.commit().setMessage("Ancestor 2").call();
+
+ // second branch
+ git.checkout().setCreateBranch(true).setStartPoint(commitA1).setName("second-branch").call();
+ writeTrashFile("a/content", "content in Child 2 (commited on second-branch)");
+ git.add().addFilepattern("a/content").call();
+ // commit C2S
+ git.commit().setMessage("Child 2 on second-branch").call();
+
+ // Merge branch-to-merge into second-branch
+ MergeResult mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+ assertEquals(mergeResult.getNewHead(), null);
+ assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+ // Resolve the conflict manually - write a file
+ git.rm().addFilepattern("a").call();
+ git.rm().addFilepattern("a/content").call();
+ deleteTrashFile("a/content");
+ deleteTrashFile("a");
+ writeTrashFile("a", "content in Child 3 (commited on second-branch) - merge conflict resolution");
+ git.add().addFilepattern("a").call();
+ RevCommit commitC3S = git.commit().setMessage("Child 3 on second bug - resolve merge conflict").call();
+
+ // Merge branch-to-merge into master
+ git.checkout().setName("master").call();
+ mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+ assertEquals(mergeResult.getNewHead(), null);
+ assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+
+ // Resolve the conflict manually - write a file
+ git.rm().addFilepattern("a").call();
+ git.rm().addFilepattern("a/content").call();
+ deleteTrashFile("a/content");
+ deleteTrashFile("a");
+ writeTrashFile("a", "content in Child 4 (commited on master) - merge conflict resolution");
+ git.add().addFilepattern("a").call();
+ // commit C4M
+ git.commit().setMessage("Child 4 on master - resolve merge conflict").call();
+
+ // Merge C4M (second-branch) into master (C3S)
+ // Conflict in virtual base should be here
+ mergeResult = git.merge().include(commitC3S).call();
+ assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+ String expected = "<<<<<<< HEAD\n" + "content in Child 4 (commited on master) - merge conflict resolution\n"
+ + "=======\n" + "content in Child 3 (commited on second-branch) - merge conflict resolution\n"
+ + ">>>>>>> " + commitC3S.name() + "\n";
+ assertEquals(expected, read("a"));
+ // Nothing was populated from the ancestors.
+ assertEquals(
+ "[a, mode:100644, stage:2, content:content in Child 4 (commited on master) - merge conflict resolution][a, mode:100644, stage:3, content:content in Child 3 (commited on second-branch) - merge conflict resolution]",
+ indexState(CONTENT));
+ }
+
private void writeSubmodule(String path, ObjectId commit)
throws IOException, ConfigInvalidException {
addSubmoduleToIndex(path, commit);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java
index a9dfe15c97..852d18c351 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java
@@ -59,20 +59,26 @@ public class DateRevQueueTest extends RevQueueTestCase<DateRevQueue> {
public void testInsertTie() throws Exception {
final RevCommit a = parseBody(commit());
final RevCommit b = parseBody(commit(0, a));
+ final RevCommit c = parseBody(commit(0, b));
+
{
q = create();
q.add(a);
q.add(b);
+ q.add(c);
assertCommit(a, q.next());
assertCommit(b, q.next());
+ assertCommit(c, q.next());
assertNull(q.next());
}
{
q = create();
+ q.add(c);
q.add(b);
q.add(a);
+ assertCommit(c, q.next());
assertCommit(b, q.next());
assertCommit(a, q.next());
assertNull(q.next());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java
index b92a0726ee..a3ba3d67b2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2010, Google Inc. and others
+ * Copyright (C) 2008, 2020, Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -11,6 +11,7 @@
package org.eclipse.jgit.revwalk;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -18,6 +19,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.junit.RepositoryTestCase;
@@ -117,6 +119,7 @@ public class RevTagParseTest extends RepositoryTestCase {
assertNotNull(c.getTagName());
assertEquals(name, c.getTagName());
assertEquals("", c.getFullMessage());
+ assertNull(c.getRawGpgSignature());
final PersonIdent cTagger = c.getTaggerIdent();
assertNotNull(cTagger);
@@ -128,13 +131,12 @@ public class RevTagParseTest extends RepositoryTestCase {
public void testParseOldStyleNoTagger() throws Exception {
final ObjectId treeId = id("9788669ad918b6fcce64af8882fc9a81cb6aba67");
final String name = "v1.2.3.4.5";
- final String message = "test\n" //
- + "\n" //
- + "-----BEGIN PGP SIGNATURE-----\n" //
+ final String fakeSignature = "-----BEGIN PGP SIGNATURE-----\n" //
+ "Version: GnuPG v1.4.1 (GNU/Linux)\n" //
+ "\n" //
+ "iD8DBQBC0b9oF3Y\n" //
- + "-----END PGP SIGNATURE------n";
+ + "-----END PGP SIGNATURE-----";
+ final String message = "test\n" + fakeSignature + '\n';
final StringBuilder body = new StringBuilder();
@@ -166,7 +168,9 @@ public class RevTagParseTest extends RepositoryTestCase {
assertNotNull(c.getTagName());
assertEquals(name, c.getTagName());
assertEquals("test", c.getShortMessage());
- assertEquals(message, c.getFullMessage());
+ assertEquals("test\n", c.getFullMessage());
+ assertEquals(fakeSignature + '\n',
+ new String(c.getRawGpgSignature(), US_ASCII));
assertNull(c.getTaggerIdent());
}
@@ -386,6 +390,108 @@ public class RevTagParseTest extends RepositoryTestCase {
}
@Test
+ public void testParse_gpgSignature() throws Exception {
+ final String signature = "-----BEGIN PGP SIGNATURE-----\n\n"
+ + "wsBcBAABCAAQBQJbGB4pCRBK7hj4Ov3rIwAAdHIIAENrvz23867ZgqrmyPemBEZP\n"
+ + "U24B1Tlq/DWvce2buaxmbNQngKZ0pv2s8VMc11916WfTIC9EKvioatmpjduWvhqj\n"
+ + "znQTFyiMor30pyYsfrqFuQZvqBW01o8GEWqLg8zjf9Rf0R3LlOEw86aT8CdHRlm6\n"
+ + "wlb22xb8qoX4RB+LYfz7MhK5F+yLOPXZdJnAVbuyoMGRnDpwdzjL5Hj671+XJxN5\n"
+ + "SasRdhxkkfw/ZnHxaKEc4juMz8Nziz27elRwhOQqlTYoXNJnsV//wy5Losd7aKi1\n"
+ + "xXXyUpndEOmT0CIcKHrN/kbYoVL28OJaxoBuva3WYQaRrzEe3X02NMxZe9gkSqA=\n"
+ + "=TClh\n" + "-----END PGP SIGNATURE-----";
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+ .getBytes(UTF_8));
+ b.write("type tree\n".getBytes(UTF_8));
+ b.write("tag v1.0\n".getBytes(UTF_8));
+ b.write("tagger t <t@example.com> 1218123387 +0700\n".getBytes(UTF_8));
+ b.write('\n');
+ b.write("message\n".getBytes(UTF_8));
+ b.write(signature.getBytes(US_ASCII));
+ b.write('\n');
+
+ RevTag t = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
+ try (RevWalk rw = new RevWalk(db)) {
+ t.parseCanonical(rw, b.toByteArray());
+ }
+
+ assertEquals("t", t.getTaggerIdent().getName());
+ assertEquals("message", t.getShortMessage());
+ assertEquals("message\n", t.getFullMessage());
+ String gpgSig = new String(t.getRawGpgSignature(), UTF_8);
+ assertEquals(signature + '\n', gpgSig);
+ }
+
+ @Test
+ public void testParse_gpgSignature2() throws Exception {
+ final String signature = "-----BEGIN PGP SIGNATURE-----\n\n"
+ + "wsBcBAABCAAQBQJbGB4pCRBK7hj4Ov3rIwAAdHIIAENrvz23867ZgqrmyPemBEZP\n"
+ + "U24B1Tlq/DWvce2buaxmbNQngKZ0pv2s8VMc11916WfTIC9EKvioatmpjduWvhqj\n"
+ + "znQTFyiMor30pyYsfrqFuQZvqBW01o8GEWqLg8zjf9Rf0R3LlOEw86aT8CdHRlm6\n"
+ + "wlb22xb8qoX4RB+LYfz7MhK5F+yLOPXZdJnAVbuyoMGRnDpwdzjL5Hj671+XJxN5\n"
+ + "SasRdhxkkfw/ZnHxaKEc4juMz8Nziz27elRwhOQqlTYoXNJnsV//wy5Losd7aKi1\n"
+ + "xXXyUpndEOmT0CIcKHrN/kbYoVL28OJaxoBuva3WYQaRrzEe3X02NMxZe9gkSqA=\n"
+ + "=TClh\n" + "-----END PGP SIGNATURE-----";
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+ .getBytes(UTF_8));
+ b.write("type tree\n".getBytes(UTF_8));
+ b.write("tag v1.0\n".getBytes(UTF_8));
+ b.write("tagger t <t@example.com> 1218123387 +0700\n".getBytes(UTF_8));
+ b.write('\n');
+ String message = "message\n\n" + signature.replace("xXXy", "aAAb")
+ + '\n';
+ b.write(message.getBytes(UTF_8));
+ b.write(signature.getBytes(US_ASCII));
+ b.write('\n');
+
+ RevTag t = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
+ try (RevWalk rw = new RevWalk(db)) {
+ t.parseCanonical(rw, b.toByteArray());
+ }
+
+ assertEquals("t", t.getTaggerIdent().getName());
+ assertEquals("message", t.getShortMessage());
+ assertEquals(message, t.getFullMessage());
+ String gpgSig = new String(t.getRawGpgSignature(), UTF_8);
+ assertEquals(signature + '\n', gpgSig);
+ }
+
+ @Test
+ public void testParse_gpgSignature3() throws Exception {
+ final String signature = "-----BEGIN PGP SIGNATURE-----\n\n"
+ + "wsBcBAABCAAQBQJbGB4pCRBK7hj4Ov3rIwAAdHIIAENrvz23867ZgqrmyPemBEZP\n"
+ + "U24B1Tlq/DWvce2buaxmbNQngKZ0pv2s8VMc11916WfTIC9EKvioatmpjduWvhqj\n"
+ + "znQTFyiMor30pyYsfrqFuQZvqBW01o8GEWqLg8zjf9Rf0R3LlOEw86aT8CdHRlm6\n"
+ + "wlb22xb8qoX4RB+LYfz7MhK5F+yLOPXZdJnAVbuyoMGRnDpwdzjL5Hj671+XJxN5\n"
+ + "SasRdhxkkfw/ZnHxaKEc4juMz8Nziz27elRwhOQqlTYoXNJnsV//wy5Losd7aKi1\n"
+ + "xXXyUpndEOmT0CIcKHrN/kbYoVL28OJaxoBuva3WYQaRrzEe3X02NMxZe9gkSqA=\n"
+ + "=TClh\n" + "-----END PGP SIGNATURE-----";
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+ .getBytes(UTF_8));
+ b.write("type tree\n".getBytes(UTF_8));
+ b.write("tag v1.0\n".getBytes(UTF_8));
+ b.write("tagger t <t@example.com> 1218123387 +0700\n".getBytes(UTF_8));
+ b.write('\n');
+ String message = "message\n\n-----BEGIN PGP SIGNATURE-----\n";
+ b.write(message.getBytes(UTF_8));
+ b.write(signature.getBytes(US_ASCII));
+ b.write('\n');
+
+ RevTag t = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
+ try (RevWalk rw = new RevWalk(db)) {
+ t.parseCanonical(rw, b.toByteArray());
+ }
+
+ assertEquals("t", t.getTaggerIdent().getName());
+ assertEquals("message", t.getShortMessage());
+ assertEquals(message, t.getFullMessage());
+ String gpgSig = new String(t.getRawGpgSignature(), UTF_8);
+ assertEquals(signature + '\n', gpgSig);
+ }
+
+ @Test
public void testParse_NoMessage() throws Exception {
final String msg = "";
final RevTag c = create(msg);
@@ -447,7 +553,8 @@ public class RevTagParseTest extends RepositoryTestCase {
}
@Test
- public void testParse_PublicParseMethod() throws CorruptObjectException {
+ public void testParse_PublicParseMethod()
+ throws CorruptObjectException, UnsupportedEncodingException {
TagBuilder src = new TagBuilder();
try (ObjectInserter.Formatter fmt = new ObjectInserter.Formatter()) {
src.setObjectId(fmt.idFor(Constants.OBJ_TREE, new byte[] {}),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java
index 803ff108f8..b8b503cdcd 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java
@@ -13,6 +13,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.util.FileUtils.pathToString;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -178,7 +179,7 @@ public class FileBasedConfigTest {
final Path includedFile = createFile(CONTENT1.getBytes(UTF_8), "dir1");
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write("[include]\npath=".getBytes(UTF_8));
- bos.write(("../" + includedFile.getParent().getFileName() + "/"
+ bos.write(("../" + parent(includedFile).getFileName() + "/"
+ includedFile.getFileName()).getBytes(UTF_8));
final Path file = createFile(bos.toByteArray(), "dir2");
@@ -213,7 +214,7 @@ public class FileBasedConfigTest {
final Path file = createFile(bos.toByteArray(), "repo");
final FS fs = FS.DETECTED.newInstance();
- fs.setUserHome(includedFile.getParent().toFile());
+ fs.setUserHome(parent(includedFile).toFile());
final FileBasedConfig config = new FileBasedConfig(file.toFile(), fs);
config.load();
@@ -231,7 +232,7 @@ public class FileBasedConfigTest {
FileBasedConfig config = new FileBasedConfig(file.toFile(),
FS.DETECTED);
config.setString("include", null, "path",
- ("../" + includedFile.getParent().getFileName() + "/"
+ ("../" + parent(includedFile).getFileName() + "/"
+ includedFile.getFileName()));
// just by setting the include.path, it won't be included
@@ -280,4 +281,10 @@ public class FileBasedConfigTest {
}
return f;
}
+
+ private Path parent(Path file) {
+ Path parent = file.getParent();
+ assertNotNull(parent);
+ return parent;
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BasePackConnectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BasePackConnectionTest.java
index 64b16f659a..7d438c1dd8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BasePackConnectionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BasePackConnectionTest.java
@@ -16,11 +16,11 @@ import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.Ref;
@@ -30,18 +30,6 @@ import org.junit.Test;
public class BasePackConnectionTest {
@Test
- public void testExtractSymRefsFromCapabilities() {
- final Map<String, String> symRefs = BasePackConnection
- .extractSymRefsFromCapabilities(
- Arrays.asList("symref=HEAD:refs/heads/main",
- "symref=refs/heads/sym:refs/heads/other"));
-
- assertEquals(2, symRefs.size());
- assertEquals("refs/heads/main", symRefs.get("HEAD"));
- assertEquals("refs/heads/other", symRefs.get("refs/heads/sym"));
- }
-
- @Test
public void testUpdateWithSymRefsAdds() {
final Ref mainRef = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE,
"refs/heads/main", ObjectId.fromString(
@@ -230,4 +218,30 @@ public class BasePackConnectionTest {
assertThat(refMap, not(hasKey("refs/heads/sym1")));
assertThat(refMap, not(hasKey("refs/heads/sym2")));
}
+
+ @Test
+ public void testUpdateWithSymRefsFillInHead() {
+ final String oidName = "0000000000000000000000000000000000000001";
+ final Ref advertised = new ObjectIdRef.PeeledNonTag(Ref.Storage.NETWORK,
+ Constants.HEAD, ObjectId.fromString(oidName));
+
+ final Map<String, Ref> refMap = new HashMap<>();
+ refMap.put(advertised.getName(), advertised);
+
+ final Map<String, String> symRefs = new HashMap<>();
+ symRefs.put("HEAD", "refs/heads/main");
+
+ BasePackConnection.updateWithSymRefs(refMap, symRefs);
+
+ assertThat(refMap, hasKey("HEAD"));
+ assertThat(refMap, hasKey("refs/heads/main"));
+ final Ref headRef = refMap.get("HEAD");
+ final Ref mainRef = refMap.get("refs/heads/main");
+ assertThat(headRef, instanceOf(SymbolicRef.class));
+ final SymbolicRef headSymRef = (SymbolicRef) headRef;
+ assertEquals(Constants.HEAD, headSymRef.getName());
+ assertSame(mainRef, headSymRef.getTarget());
+ assertEquals(oidName, headRef.getObjectId().name());
+ assertEquals(oidName, mainRef.getObjectId().name());
+ }
} \ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
index 07c236daba..60b8098b31 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
@@ -29,7 +29,7 @@ import java.util.zip.Deflater;
import org.eclipse.jgit.errors.TooLargeObjectInPackException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.ObjectDirectoryPackParser;
-import org.eclipse.jgit.internal.storage.file.PackFile;
+import org.eclipse.jgit.internal.storage.file.Pack;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
@@ -63,16 +63,16 @@ public class PackParserTest extends RepositoryTestCase {
try (InputStream is = new FileInputStream(packFile)) {
ObjectDirectoryPackParser p = (ObjectDirectoryPackParser) index(is);
p.parse(NullProgressMonitor.INSTANCE);
- PackFile file = p.getPackFile();
-
- assertTrue(file.hasObject(ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904")));
- assertTrue(file.hasObject(ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab")));
- assertTrue(file.hasObject(ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259")));
- assertTrue(file.hasObject(ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3")));
- assertTrue(file.hasObject(ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")));
- assertTrue(file.hasObject(ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327")));
- assertTrue(file.hasObject(ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035")));
- assertTrue(file.hasObject(ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799")));
+ Pack pack = p.getPack();
+
+ assertTrue(pack.hasObject(ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904")));
+ assertTrue(pack.hasObject(ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab")));
+ assertTrue(pack.hasObject(ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259")));
+ assertTrue(pack.hasObject(ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3")));
+ assertTrue(pack.hasObject(ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")));
+ assertTrue(pack.hasObject(ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327")));
+ assertTrue(pack.hasObject(ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035")));
+ assertTrue(pack.hasObject(ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799")));
}
}
@@ -88,20 +88,20 @@ public class PackParserTest extends RepositoryTestCase {
try (InputStream is = new FileInputStream(packFile)) {
ObjectDirectoryPackParser p = (ObjectDirectoryPackParser) index(is);
p.parse(NullProgressMonitor.INSTANCE);
- PackFile file = p.getPackFile();
-
- assertTrue(file.hasObject(ObjectId.fromString("02ba32d3649e510002c21651936b7077aa75ffa9")));
- assertTrue(file.hasObject(ObjectId.fromString("0966a434eb1a025db6b71485ab63a3bfbea520b6")));
- assertTrue(file.hasObject(ObjectId.fromString("09efc7e59a839528ac7bda9fa020dc9101278680")));
- assertTrue(file.hasObject(ObjectId.fromString("0a3d7772488b6b106fb62813c4d6d627918d9181")));
- assertTrue(file.hasObject(ObjectId.fromString("1004d0d7ac26fbf63050a234c9b88a46075719d3")));
- assertTrue(file.hasObject(ObjectId.fromString("10da5895682013006950e7da534b705252b03be6")));
- assertTrue(file.hasObject(ObjectId.fromString("1203b03dc816ccbb67773f28b3c19318654b0bc8")));
- assertTrue(file.hasObject(ObjectId.fromString("15fae9e651043de0fd1deef588aa3fbf5a7a41c6")));
- assertTrue(file.hasObject(ObjectId.fromString("16f9ec009e5568c435f473ba3a1df732d49ce8c3")));
- assertTrue(file.hasObject(ObjectId.fromString("1fd7d579fb6ae3fe942dc09c2c783443d04cf21e")));
- assertTrue(file.hasObject(ObjectId.fromString("20a8ade77639491ea0bd667bf95de8abf3a434c8")));
- assertTrue(file.hasObject(ObjectId.fromString("2675188fd86978d5bc4d7211698b2118ae3bf658")));
+ Pack pack = p.getPack();
+
+ assertTrue(pack.hasObject(ObjectId.fromString("02ba32d3649e510002c21651936b7077aa75ffa9")));
+ assertTrue(pack.hasObject(ObjectId.fromString("0966a434eb1a025db6b71485ab63a3bfbea520b6")));
+ assertTrue(pack.hasObject(ObjectId.fromString("09efc7e59a839528ac7bda9fa020dc9101278680")));
+ assertTrue(pack.hasObject(ObjectId.fromString("0a3d7772488b6b106fb62813c4d6d627918d9181")));
+ assertTrue(pack.hasObject(ObjectId.fromString("1004d0d7ac26fbf63050a234c9b88a46075719d3")));
+ assertTrue(pack.hasObject(ObjectId.fromString("10da5895682013006950e7da534b705252b03be6")));
+ assertTrue(pack.hasObject(ObjectId.fromString("1203b03dc816ccbb67773f28b3c19318654b0bc8")));
+ assertTrue(pack.hasObject(ObjectId.fromString("15fae9e651043de0fd1deef588aa3fbf5a7a41c6")));
+ assertTrue(pack.hasObject(ObjectId.fromString("16f9ec009e5568c435f473ba3a1df732d49ce8c3")));
+ assertTrue(pack.hasObject(ObjectId.fromString("1fd7d579fb6ae3fe942dc09c2c783443d04cf21e")));
+ assertTrue(pack.hasObject(ObjectId.fromString("20a8ade77639491ea0bd667bf95de8abf3a434c8")));
+ assertTrue(pack.hasObject(ObjectId.fromString("2675188fd86978d5bc4d7211698b2118ae3bf658")));
// and lots more...
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java
index 5d7f881ab9..505e0088c4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009, Google Inc. and others
+ * Copyright (C) 2009, 2020 Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -12,8 +12,9 @@ package org.eclipse.jgit.transport;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
@@ -111,20 +112,25 @@ public class PacketLineInTest {
final String act = in.readString();
assertEquals("", act);
assertFalse(PacketLineIn.isEnd(act));
+ assertFalse(PacketLineIn.isDelimiter(act));
assertEOF();
}
@Test
public void testReadString_End() throws IOException {
init("0000");
- assertTrue(PacketLineIn.isEnd(in.readString()));
+ String act = in.readString();
+ assertTrue(PacketLineIn.isEnd(act));
+ assertFalse(PacketLineIn.isDelimiter(act));
assertEOF();
}
@Test
public void testReadString_Delim() throws IOException {
init("0001");
- assertTrue(PacketLineIn.isDelimiter(in.readString()));
+ String act = in.readString();
+ assertTrue(PacketLineIn.isDelimiter(act));
+ assertFalse(PacketLineIn.isEnd(act));
assertEOF();
}
@@ -292,6 +298,58 @@ public class PacketLineInTest {
}
}
+ // parseACKv2
+
+ @Test
+ public void testParseAckV2_NAK() throws IOException {
+ final ObjectId expid = ObjectId
+ .fromString("fcfcfb1fd94829c1a1704f894fc111d14770d34e");
+ final MutableObjectId actid = new MutableObjectId();
+ actid.fromString(expid.name());
+
+ assertSame(PacketLineIn.AckNackResult.NAK,
+ PacketLineIn.parseACKv2("NAK", actid));
+ assertEquals(expid, actid);
+ }
+
+ @Test
+ public void testParseAckV2_ACK() throws IOException {
+ final ObjectId expid = ObjectId
+ .fromString("fcfcfb1fd94829c1a1704f894fc111d14770d34e");
+ final MutableObjectId actid = new MutableObjectId();
+
+ assertSame(PacketLineIn.AckNackResult.ACK_COMMON,
+ PacketLineIn.parseACKv2(
+ "ACK fcfcfb1fd94829c1a1704f894fc111d14770d34e", actid));
+ assertEquals(expid, actid);
+ }
+
+ @Test
+ public void testParseAckV2_Ready() throws IOException {
+ final ObjectId expid = ObjectId
+ .fromString("fcfcfb1fd94829c1a1704f894fc111d14770d34e");
+ final MutableObjectId actid = new MutableObjectId();
+ actid.fromString(expid.name());
+
+ assertSame(PacketLineIn.AckNackResult.ACK_READY,
+ PacketLineIn.parseACKv2("ready", actid));
+ assertEquals(expid, actid);
+ }
+
+ @Test
+ public void testParseAckV2_ERR() {
+ IOException e = assertThrows(IOException.class, () -> PacketLineIn
+ .parseACKv2("ERR want is not valid", new MutableObjectId()));
+ assertTrue(e.getMessage().contains("want is not valid"));
+ }
+
+ @Test
+ public void testParseAckV2_Invalid() {
+ IOException e = assertThrows(IOException.class,
+ () -> PacketLineIn.parseACKv2("HELO", new MutableObjectId()));
+ assertTrue(e.getMessage().contains("xpected ACK/NAK"));
+ }
+
// test support
private void init(String msg) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransferConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransferConfigTest.java
new file mode 100644
index 0000000000..d9b85fb003
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransferConfigTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.eclipse.jgit.lib.Config;
+import org.junit.Test;
+
+/**
+ * Tests for {@link TransferConfig} parsing.
+ */
+public class TransferConfigTest {
+
+ @Test
+ public void testParseProtocolV0() {
+ Config rc = new Config();
+ rc.setInt("protocol", null, "version", 0);
+ TransferConfig tc = new TransferConfig(rc);
+ assertEquals(TransferConfig.ProtocolVersion.V0, tc.protocolVersion);
+ }
+
+ @Test
+ public void testParseProtocolV1() {
+ Config rc = new Config();
+ rc.setInt("protocol", null, "version", 1);
+ TransferConfig tc = new TransferConfig(rc);
+ assertEquals(TransferConfig.ProtocolVersion.V0, tc.protocolVersion);
+ }
+
+ @Test
+ public void testParseProtocolV2() {
+ Config rc = new Config();
+ rc.setInt("protocol", null, "version", 2);
+ TransferConfig tc = new TransferConfig(rc);
+ assertEquals(TransferConfig.ProtocolVersion.V2, tc.protocolVersion);
+ }
+
+ @Test
+ public void testParseProtocolNotSet() {
+ Config rc = new Config();
+ TransferConfig tc = new TransferConfig(rc);
+ assertNull(tc.protocolVersion);
+ }
+
+ @Test
+ public void testParseProtocolUnknown() {
+ Config rc = new Config();
+ rc.setInt("protocol", null, "version", 3);
+ TransferConfig tc = new TransferConfig(rc);
+ assertNull(tc.protocolVersion);
+ }
+
+ @Test
+ public void testParseProtocolInvalid() {
+ Config rc = new Config();
+ rc.setString("protocol", null, "version", "foo");
+ TransferConfig tc = new TransferConfig(rc);
+ assertNull(tc.protocolVersion);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
index ce546e357e..5045e9464e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
@@ -38,6 +38,7 @@ import org.eclipse.jgit.internal.storage.file.PackLock;
import org.eclipse.jgit.internal.storage.pack.CachedPack;
import org.eclipse.jgit.internal.storage.pack.CachedPackUriProvider;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -370,7 +371,8 @@ public class UploadPackTest {
ByteArrayInputStream send = linesAsInputStream(inputLines);
- server.getConfig().setString("protocol", null, "version", version);
+ server.getConfig().setString(ConfigConstants.CONFIG_PROTOCOL_SECTION,
+ null, ConfigConstants.CONFIG_KEY_VERSION, version);
UploadPack up = new UploadPack(server);
if (postConstructionSetup != null) {
postConstructionSetup.accept(up);
@@ -435,8 +437,9 @@ public class UploadPackTest {
Consumer<UploadPack> postConstructionSetup,
String... inputLines)
throws Exception {
- ByteArrayInputStream recvStream =
- uploadPackSetup("2", postConstructionSetup, inputLines);
+ ByteArrayInputStream recvStream = uploadPackSetup(
+ TransferConfig.ProtocolVersion.V2.version(),
+ postConstructionSetup, inputLines);
PacketLineIn pckIn = new PacketLineIn(recvStream);
// drain capabilities
@@ -476,9 +479,11 @@ public class UploadPackTest {
@Test
public void testV2Capabilities() throws Exception {
TestV2Hook hook = new TestV2Hook();
- ByteArrayInputStream recvStream = uploadPackSetup( "2",
- (UploadPack up) -> {up.setProtocolV2Hook(hook);},
- PacketLineIn.end());
+ ByteArrayInputStream recvStream = uploadPackSetup(
+ TransferConfig.ProtocolVersion.V2.version(),
+ (UploadPack up) -> {
+ up.setProtocolV2Hook(hook);
+ }, PacketLineIn.end());
PacketLineIn pckIn = new PacketLineIn(recvStream);
assertThat(hook.capabilitiesRequest, notNullValue());
assertThat(pckIn.readString(), is("version 2"));
@@ -498,8 +503,9 @@ public class UploadPackTest {
private void checkAdvertisedIfAllowed(String configSection, String configName,
String fetchCapability) throws Exception {
server.getConfig().setBoolean(configSection, null, configName, true);
- ByteArrayInputStream recvStream =
- uploadPackSetup("2", null, PacketLineIn.end());
+ ByteArrayInputStream recvStream = uploadPackSetup(
+ TransferConfig.ProtocolVersion.V2.version(), null,
+ PacketLineIn.end());
PacketLineIn pckIn = new PacketLineIn(recvStream);
assertThat(pckIn.readString(), is("version 2"));
@@ -522,8 +528,9 @@ public class UploadPackTest {
private void checkUnadvertisedIfUnallowed(String configSection,
String configName, String fetchCapability) throws Exception {
server.getConfig().setBoolean(configSection, null, configName, false);
- ByteArrayInputStream recvStream =
- uploadPackSetup("2", null, PacketLineIn.end());
+ ByteArrayInputStream recvStream = uploadPackSetup(
+ TransferConfig.ProtocolVersion.V2.version(), null,
+ PacketLineIn.end());
PacketLineIn pckIn = new PacketLineIn(recvStream);
assertThat(pckIn.readString(), is("version 2"));
@@ -574,8 +581,9 @@ public class UploadPackTest {
public void testV2CapabilitiesRefInWantNotAdvertisedIfAdvertisingForbidden() throws Exception {
server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
server.getConfig().setBoolean("uploadpack", null, "advertiserefinwant", false);
- ByteArrayInputStream recvStream =
- uploadPackSetup("2", null, PacketLineIn.end());
+ ByteArrayInputStream recvStream = uploadPackSetup(
+ TransferConfig.ProtocolVersion.V2.version(), null,
+ PacketLineIn.end());
PacketLineIn pckIn = new PacketLineIn(recvStream);
assertThat(pckIn.readString(), is("version 2"));
@@ -739,7 +747,10 @@ public class UploadPackTest {
PacketLineIn.end() };
TestV2Hook testHook = new TestV2Hook();
- uploadPackSetup("2", (UploadPack up) -> {up.setProtocolV2Hook(testHook);}, lines);
+ uploadPackSetup(TransferConfig.ProtocolVersion.V2.version(),
+ (UploadPack up) -> {
+ up.setProtocolV2Hook(testHook);
+ }, lines);
LsRefsV2Request req = testHook.lsRefsRequest;
assertEquals(2, req.getServerOptions().size());
@@ -1559,7 +1570,10 @@ public class UploadPackTest {
PacketLineIn.end() };
TestV2Hook testHook = new TestV2Hook();
- uploadPackSetup("2", (UploadPack up) -> {up.setProtocolV2Hook(testHook);}, lines);
+ uploadPackSetup(TransferConfig.ProtocolVersion.V2.version(),
+ (UploadPack up) -> {
+ up.setProtocolV2Hook(testHook);
+ }, lines);
FetchV2Request req = testHook.fetchRequest;
assertNotNull(req);
@@ -2253,7 +2267,9 @@ public class UploadPackTest {
@Test
public void testGetPeerAgentProtocolV2() throws Exception {
- server.getConfig().setString("protocol", null, "version", "2");
+ server.getConfig().setString(ConfigConstants.CONFIG_PROTOCOL_SECTION,
+ null, ConfigConstants.CONFIG_KEY_VERSION,
+ TransferConfig.ProtocolVersion.V2.version());
RevCommit one = remote.commit().message("1").create();
remote.update("one", one);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOTest.java
new file mode 100644
index 0000000000..10a858fbfb
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.util;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+import org.junit.Test;
+
+public class IOTest {
+
+ private static final byte[] DATA = "abcdefghijklmnopqrstuvwxyz"
+ .getBytes(StandardCharsets.US_ASCII);
+
+ private byte[] initBuffer(int size) {
+ byte[] buffer = new byte[size];
+ for (int i = 0; i < size; i++) {
+ buffer[i] = (byte) ('0' + (i % 10));
+ }
+ return buffer;
+ }
+
+ private int read(byte[] buffer, int from) throws IOException {
+ try (InputStream in = new ByteArrayInputStream(DATA)) {
+ return IO.readFully(in, buffer, from);
+ }
+ }
+
+ @Test
+ public void readFullyBufferShorter() throws Exception {
+ byte[] buffer = initBuffer(9);
+ int length = read(buffer, 0);
+ assertEquals(buffer.length, length);
+ assertArrayEquals(buffer, Arrays.copyOfRange(DATA, 0, length));
+ }
+
+ @Test
+ public void readFullyBufferLonger() throws Exception {
+ byte[] buffer = initBuffer(50);
+ byte[] initial = Arrays.copyOf(buffer, buffer.length);
+ int length = read(buffer, 0);
+ assertEquals(DATA.length, length);
+ assertArrayEquals(Arrays.copyOfRange(buffer, 0, length), DATA);
+ assertArrayEquals(Arrays.copyOfRange(buffer, length, buffer.length),
+ Arrays.copyOfRange(initial, length, initial.length));
+ }
+
+ @Test
+ public void readFullyBufferShorterOffset() throws Exception {
+ byte[] buffer = initBuffer(9);
+ byte[] initial = Arrays.copyOf(buffer, buffer.length);
+ int length = read(buffer, 6);
+ assertEquals(3, length);
+ assertArrayEquals(Arrays.copyOfRange(buffer, 0, 6),
+ Arrays.copyOfRange(initial, 0, 6));
+ assertArrayEquals(Arrays.copyOfRange(buffer, 6, buffer.length),
+ Arrays.copyOfRange(DATA, 0, 3));
+ }
+
+ @Test
+ public void readFullyBufferLongerOffset() throws Exception {
+ byte[] buffer = initBuffer(50);
+ byte[] initial = Arrays.copyOf(buffer, buffer.length);
+ int length = read(buffer, 40);
+ assertEquals(10, length);
+ assertArrayEquals(Arrays.copyOfRange(buffer, 0, 40),
+ Arrays.copyOfRange(initial, 0, 40));
+ assertArrayEquals(Arrays.copyOfRange(buffer, 40, buffer.length),
+ Arrays.copyOfRange(DATA, 0, 10));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java
index 4e65ca7a4b..01dcde29bd 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java
@@ -406,4 +406,69 @@ public class TemporaryBufferTest {
}
}
}
+
+ @Test
+ public void testHeapToByteArrayWithLimit() throws IOException {
+ int sz = 2 * Block.SZ;
+ try (TemporaryBuffer b = new TemporaryBuffer.Heap(sz / 2, sz)) {
+ for (int i = 0; i < sz; i++) {
+ b.write('a' + i % 26);
+ }
+ byte[] prefix = b.toByteArray(5);
+ assertEquals(5, prefix.length);
+ for (int i = 0; i < prefix.length; i++) {
+ assertEquals('a' + i % 26, prefix[i]);
+ }
+ prefix = b.toByteArray(Block.SZ + 37);
+ assertEquals(Block.SZ + 37, prefix.length);
+ for (int i = 0; i < prefix.length; i++) {
+ assertEquals('a' + i % 26, prefix[i]);
+ }
+ prefix = b.toByteArray(sz);
+ assertEquals(sz, prefix.length);
+ for (int i = 0; i < prefix.length; i++) {
+ assertEquals('a' + i % 26, prefix[i]);
+ }
+ prefix = b.toByteArray(sz + 37);
+ assertEquals(sz, prefix.length);
+ for (int i = 0; i < prefix.length; i++) {
+ assertEquals('a' + i % 26, prefix[i]);
+ }
+ }
+ }
+
+ @Test
+ public void testFileToByteArrayWithLimit() throws IOException {
+ @SuppressWarnings("resource") // Buffer is explicitly destroyed in finally block
+ TemporaryBuffer b = new TemporaryBuffer.LocalFile(null, 2 * Block.SZ);
+ int sz = 3 * Block.SZ;
+ try {
+ for (int i = 0; i < sz; i++) {
+ b.write('a' + i % 26);
+ }
+ b.close();
+ byte[] prefix = b.toByteArray(5);
+ assertEquals(5, prefix.length);
+ for (int i = 0; i < prefix.length; i++) {
+ assertEquals('a' + i % 26, prefix[i]);
+ }
+ prefix = b.toByteArray(Block.SZ + 37);
+ assertEquals(Block.SZ + 37, prefix.length);
+ for (int i = 0; i < prefix.length; i++) {
+ assertEquals('a' + i % 26, prefix[i]);
+ }
+ prefix = b.toByteArray(sz);
+ assertEquals(sz, prefix.length);
+ for (int i = 0; i < prefix.length; i++) {
+ assertEquals('a' + i % 26, prefix[i]);
+ }
+ prefix = b.toByteArray(sz + 37);
+ assertEquals(sz, prefix.length);
+ for (int i = 0; i < prefix.length; i++) {
+ assertEquals('a' + i % 26, prefix[i]);
+ }
+ } finally {
+ b.destroy();
+ }
+ }
}
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 538c6f7bfb..d389ac5888 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -1,38 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.jgit" version="2">
- <resource path="src/org/eclipse/jgit/lib/TypedConfigGetter.java" type="org.eclipse.jgit.lib.TypedConfigGetter">
- <filter id="404000815">
+ <resource path="src/org/eclipse/jgit/lib/ConfigConstants.java" type="org.eclipse.jgit.lib.ConfigConstants">
+ <filter id="338755678">
<message_arguments>
- <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
- <message_argument value="getPath(Config, String, String, String, FS, File, Path)"/>
+ <message_argument value="org.eclipse.jgit.lib.ConfigConstants"/>
+ <message_argument value="CONFIG_REFSTORAGE_REFTREE"/>
</message_arguments>
</filter>
</resource>
- <resource path="src/org/eclipse/jgit/storage/pack/PackStatistics.java" type="org.eclipse.jgit.storage.pack.PackStatistics$Accumulator">
- <filter id="336658481">
+ <resource path="src/org/eclipse/jgit/revwalk/ObjectWalk.java" type="org.eclipse.jgit.revwalk.ObjectWalk">
+ <filter id="421654647">
<message_arguments>
- <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/>
- <message_argument value="notAdvertisedWants"/>
+ <message_argument value="org.eclipse.jgit.revwalk.ObjectWalk"/>
+ <message_argument value="createObjectReachabilityChecker()"/>
</message_arguments>
</filter>
- <filter id="336658481">
+ </resource>
+ <resource path="src/org/eclipse/jgit/revwalk/RevWalk.java" type="org.eclipse.jgit.revwalk.RevWalk">
+ <filter id="421654647">
<message_arguments>
- <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/>
- <message_argument value="reachabilityCheckDuration"/>
+ <message_argument value="org.eclipse.jgit.revwalk.RevWalk"/>
+ <message_argument value="createReachabilityChecker()"/>
</message_arguments>
</filter>
</resource>
- <resource path="src/org/eclipse/jgit/transport/HttpConfig.java" type="org.eclipse.jgit.transport.HttpConfig">
- <filter id="336658481">
+ <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS">
+ <filter id="338792546">
<message_arguments>
- <message_argument value="org.eclipse.jgit.transport.HttpConfig"/>
- <message_argument value="EXTRA_HEADER"/>
+ <message_argument value="org.eclipse.jgit.util.FS"/>
+ <message_argument value="internalRunHookIfPresent(Repository, String, String[], PrintStream, PrintStream, String)"/>
</message_arguments>
</filter>
- <filter id="336658481">
+ <filter id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.util.FS"/>
+ <message_argument value="runHookIfPresent(Repository, String, String[], PrintStream, PrintStream, String)"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/util/FS_POSIX.java" type="org.eclipse.jgit.util.FS_POSIX">
+ <filter id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.util.FS_POSIX"/>
+ <message_argument value="runHookIfPresent(Repository, String, String[], PrintStream, PrintStream, String)"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/util/FS_Win32_Cygwin.java" type="org.eclipse.jgit.util.FS_Win32_Cygwin">
+ <filter id="338792546">
<message_arguments>
- <message_argument value="org.eclipse.jgit.transport.HttpConfig"/>
- <message_argument value="USER_AGENT"/>
+ <message_argument value="org.eclipse.jgit.util.FS_Win32_Cygwin"/>
+ <message_argument value="runHookIfPresent(Repository, String, String[], PrintStream, PrintStream, String)"/>
</message_arguments>
</filter>
</resource>
diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD
index 2083372248..04873b0c72 100644
--- a/org.eclipse.jgit/BUILD
+++ b/org.eclipse.jgit/BUILD
@@ -38,7 +38,7 @@ genrule(
"cd $$TMP",
"unzip -q $$ROOT/$<",
"echo \"Implementation-Version: $$GEN_VERSION\n$$(cat META-INF/MANIFEST.MF)\" > META-INF/MANIFEST.MF",
- "find . -exec touch '{}' ';'",
+ "find . -exec touch -t 198001010000 '{}' ';'",
"zip -Xqr $$ROOT/$@ .",
"rm -rf $$TMP",
]),
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 403b8eed0f..d755f83626 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -72,11 +72,8 @@ Export-Package: org.eclipse.jgit.annotations;version="6.0.0",
org.eclipse.jgit.http.test",
org.eclipse.jgit.internal.fsck;version="6.0.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.ketch;version="6.0.0";
- x-friends:="org.eclipse.jgit.junit,
- org.eclipse.jgit.test,
- org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.revwalk;version="6.0.0";x-internal:=true,
+ org.eclipse.jgit.internal.revwalk;version="6.0.0";
+ x-friends:="org.eclipse.jgit.test",
org.eclipse.jgit.internal.storage.dfs;version="6.0.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.http.server,
@@ -104,10 +101,6 @@ Export-Package: org.eclipse.jgit.annotations;version="6.0.0",
org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftree;version="6.0.0";
- x-friends:="org.eclipse.jgit.junit,
- org.eclipse.jgit.test,
- org.eclipse.jgit.pgm",
org.eclipse.jgit.internal.submodule;version="6.0.0";x-internal:=true,
org.eclipse.jgit.internal.transport.connectivity;version="6.0.0";
x-friends:="org.eclipse.jgit.test",
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index e1904d5d3f..134cbacbee 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -5,4 +5,3 @@ Bundle-SymbolicName: org.eclipse.jgit.source
Bundle-Vendor: Eclipse.org - JGit
Bundle-Version: 6.0.0.qualifier
Eclipse-SourceBundle: org.eclipse.jgit;version="6.0.0.qualifier";roots="."
-
diff --git a/org.eclipse.jgit/findBugs/FindBugsExcludeFilter.xml b/org.eclipse.jgit/findBugs/FindBugsExcludeFilter.xml
index 2efbb9c1a5..73a6685564 100644
--- a/org.eclipse.jgit/findBugs/FindBugsExcludeFilter.xml
+++ b/org.eclipse.jgit/findBugs/FindBugsExcludeFilter.xml
@@ -9,6 +9,12 @@
<Bug pattern="DM_GC" />
</Match>
+ <Match>
+ <Class name="org.eclipse.jgit.internal.storage.pack.PackOutputStream" />
+ <Method name="writeHeader" />
+ <Bug pattern="NS_DANGEROUS_NON_SHORT_CIRCUIT" />
+ </Match>
+
<!-- Silence ignoring return value of mkdirs -->
<Match>
<Class name="org.eclipse.jgit.dircache.DirCacheCheckout" />
@@ -19,8 +25,26 @@
<!-- Silence the construction of our magic String instance.
-->
<Match>
- <Class name="org.eclipse.jgit.lib.Config" />
- <Bug pattern="DM_STRING_VOID_CTOR"/>
+ <Class name="org.eclipse.jgit.lib.Config" />
+ <Bug pattern="DM_STRING_VOID_CTOR"/>
+ </Match>
+
+ <Match>
+ <Class name="org.eclipse.jgit.lib.Config" />
+ <Method name="isMissing" />
+ <Bug pattern="ES_COMPARING_PARAMETER_STRING_WITH_EQ"/>
+ </Match>
+
+ <Match>
+ <Class name="org.eclipse.jgit.transport.PacketLineIn" />
+ <Method name="isDelimiter" />
+ <Bug pattern="ES_COMPARING_PARAMETER_STRING_WITH_EQ"/>
+ </Match>
+
+ <Match>
+ <Class name="org.eclipse.jgit.transport.PacketLineIn" />
+ <Method name="isEnd" />
+ <Bug pattern="ES_COMPARING_PARAMETER_STRING_WITH_EQ"/>
</Match>
<!-- Silence comparison of string by == or !=. This class is built
@@ -53,6 +77,12 @@
<Bug pattern="NP_BOOLEAN_RETURN_NULL" />
</Match>
+ <Match>
+ <Class name="org.eclipse.jgit.ignore.IgnoreNode" />
+ <Method name="checkIgnored" />
+ <Bug pattern="NP_BOOLEAN_RETURN_NULL" />
+ </Match>
+
<!-- Transport initialization works like this -->
<Match>
<Class name="org.eclipse.jgit.transport.Transport" />
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 12902b9004..c00203dd07 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -30,6 +30,8 @@ badEntryDelimiter=Bad entry delimiter
badEntryName=Bad entry name: {0}
badEscape=Bad escape: {0}
badGroupHeader=Bad group header
+badIgnorePattern=Cannot parse .gitignore pattern ''{0}''
+badIgnorePatternFull=File {0} line {1}: cannot parse pattern ''{2}'': {3}
badObjectType=Bad object type: {0}
badRef=Bad ref: {0}: {1}
badSectionEntry=Bad section entry: {0}
@@ -233,6 +235,7 @@ downloadCancelled=Download cancelled
downloadCancelledDuringIndexing=Download cancelled during indexing
duplicateAdvertisementsOf=duplicate advertisements of {0}
duplicateRef=Duplicate ref: {0}
+duplicateRefAttribute=Duplicate ref attribute: {0}
duplicateRemoteRefUpdateIsIllegal=Duplicate remote ref update is illegal. Affected remote name: {0}
duplicateStagesNotAllowed=Duplicate stages not allowed
eitherGitDirOrWorkTreeRequired=One of setGitDir or setWorkTree must be called.
@@ -310,6 +313,10 @@ headRequiredToStash=HEAD required to stash local changes
hoursAgo={0} hours ago
httpConfigCannotNormalizeURL=Cannot normalize URL path {0}: too many .. segments
httpConfigInvalidURL=Cannot parse URL from subsection http.{0} in git config; ignored.
+httpFactoryInUse=Changing the HTTP connection factory after an HTTP connection has already been opened is not allowed.
+httpPreAuthTooLate=HTTP Basic preemptive authentication cannot be set once an HTTP connection has already been opened.
+httpUserInfoDecodeError=Cannot decode user info from URL {}; ignored.
+httpWrongConnectionType=Wrong connection type: expected {0}, got {1}.
hugeIndexesAreNotSupportedByJgitYet=Huge indexes are not supported by jgit, yet
hunkBelongsToAnotherFile=Hunk belongs to another file
hunkDisconnectedFromFile=Hunk disconnected from file
@@ -563,6 +570,7 @@ refNotResolved=Ref {0} cannot be resolved
reftableDirExists=reftable dir exists and is nonempty
reftableRecordsMustIncrease=records must be increasing: last {0}, this {1}
refUpdateReturnCodeWas=RefUpdate return code was: {0}
+remoteBranchNotFound=Remote branch ''{0}'' not found in upstream origin
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
@@ -617,7 +625,9 @@ shortCompressedStreamAt=Short compressed stream at {0}
shortReadOfBlock=Short read of block.
shortReadOfOptionalDIRCExtensionExpectedAnotherBytes=Short read of optional DIRC extension {0}; expected another {1} bytes within the section.
shortSkipOfBlock=Short skip of block.
-signingNotSupportedOnTag=Signing isn't supported on tag operations yet.
+signatureVerificationError=Signature verification failed
+signatureVerificationUnavailable=No signature verifier registered
+signedTagMessageNoLf=A non-empty message of a signed tag must end in LF.
signingServiceUnavailable=Signing service is not available
similarityScoreMustBeWithinBounds=Similarity score must be between 0 and 100.
skipMustBeNonNegative=skip must be >= 0
@@ -758,6 +768,13 @@ uriNotFoundWithMessage={0} not found: {1}
URINotSupported=URI not supported: {0}
userConfigInvalid=Git config in the user's home directory {0} is invalid {1}
validatingGitModules=Validating .gitmodules files
+verifySignatureBad=BAD signature from "{0}"
+verifySignatureExpired=Expired signature from "{0}"
+verifySignatureGood=Good signature from "{0}"
+verifySignatureIssuer=issuer "{0}"
+verifySignatureKey=using key {0}
+verifySignatureMade=Signature made {0}
+verifySignatureTrust=[{0}]
walkFailure=Walk failure.
wantNoSpaceWithCapabilities=No space between oid and first capability in first want line
wantNotValid=want {0} not valid
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/ketch/KetchText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/ketch/KetchText.properties
deleted file mode 100644
index 1fbb7cb3b5..0000000000
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/ketch/KetchText.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-accepted=accepted.
-cannotFetchFromLocalReplica=cannot fetch from LocalReplica
-failed=failed!
-invalidFollowerUri=invalid follower URI
-leaderFailedToStore=leader failed to store
-localReplicaRequired=LocalReplica instance is required
-mismatchedTxnNamespace=mismatched txnNamespace; expected {0} found {1}
-outsideTxnNamespace=ref {0} is outside of txnNamespace {1}
-proposingUpdates=Proposing updates
-queuedProposalFailedToApply=queued proposal failed to apply
-starting=starting!
-unsupportedVoterCount=unsupported voter count {0}, expected one of {1}
-waitingForQueue=Waiting for queue
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
index 2c01c19c59..fdf8b80cd4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
@@ -19,7 +19,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
@@ -202,7 +201,7 @@ public class ArchiveCommand extends GitCommand<OutputStream> {
* Available archival formats (corresponding to values for
* the --format= option)
*/
- private static final ConcurrentMap<String, FormatEntry> formats =
+ private static final Map<String, FormatEntry> formats =
new ConcurrentHashMap<>();
/**
@@ -215,7 +214,7 @@ public class ArchiveCommand extends GitCommand<OutputStream> {
* @param newValue value to be associated with the key (null to remove).
* @return true if the value was replaced
*/
- private static <K, V> boolean replace(ConcurrentMap<K, V> map,
+ private static <K, V> boolean replace(Map<K, V> map,
K key, V oldValue, V newValue) {
if (oldValue == null && newValue == null) // Nothing to do.
return true;
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 aba86fc361..cf7bc1f263 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -297,6 +297,7 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
command.setTagOpt(
fetchAll ? TagOpt.FETCH_TAGS : TagOpt.AUTO_FOLLOW);
}
+ command.setInitialBranch(branch);
configure(command);
return command.call();
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 b4f7175036..31f6a31c75 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -47,6 +47,7 @@ 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.GpgObjectSigner;
import org.eclipse.jgit.lib.GpgSigner;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -120,6 +121,8 @@ public class CommitCommand extends GitCommand<RevCommit> {
private GpgSigner gpgSigner;
+ private GpgConfig gpgConfig;
+
private CredentialsProvider credentialsProvider;
/**
@@ -247,8 +250,18 @@ public class CommitCommand extends GitCommand<RevCommit> {
throw new ServiceUnavailableException(
JGitText.get().signingServiceUnavailable);
}
- gpgSigner.sign(commit, signingKey, committer,
- credentialsProvider);
+ if (gpgSigner instanceof GpgObjectSigner) {
+ ((GpgObjectSigner) gpgSigner).signObject(commit,
+ signingKey, committer, credentialsProvider,
+ gpgConfig);
+ } else {
+ if (gpgConfig.getKeyFormat() != GpgFormat.OPENPGP) {
+ throw new UnsupportedSigningFormatException(JGitText
+ .get().onlyOpenPgpSupportedForSigning);
+ }
+ gpgSigner.sign(commit, signingKey, committer,
+ credentialsProvider);
+ }
}
ObjectId commitId = odi.insert(commit);
@@ -576,7 +589,9 @@ public class CommitCommand extends GitCommand<RevCommit> {
// an explicit message
throw new NoMessageException(JGitText.get().commitMessageNotSpecified);
- GpgConfig gpgConfig = new GpgConfig(repo.getConfig());
+ if (gpgConfig == null) {
+ gpgConfig = new GpgConfig(repo.getConfig());
+ }
if (signCommit == null) {
signCommit = gpgConfig.isSignCommits() ? Boolean.TRUE
: Boolean.FALSE;
@@ -585,10 +600,6 @@ public class CommitCommand extends GitCommand<RevCommit> {
signingKey = gpgConfig.getSigningKey();
}
if (gpgSigner == null) {
- if (gpgConfig.getKeyFormat() != GpgFormat.OPENPGP) {
- throw new UnsupportedSigningFormatException(
- JGitText.get().onlyOpenPgpSupportedForSigning);
- }
gpgSigner = GpgSigner.getDefault();
}
}
@@ -973,6 +984,36 @@ public class CommitCommand extends GitCommand<RevCommit> {
}
/**
+ * Sets the {@link GpgSigner} to use if the commit is to be signed.
+ *
+ * @param signer
+ * to use; if {@code null}, the default signer will be used
+ * @return {@code this}
+ * @since 5.11
+ */
+ public CommitCommand setGpgSigner(GpgSigner signer) {
+ checkCallable();
+ this.gpgSigner = signer;
+ return this;
+ }
+
+ /**
+ * Sets an external {@link GpgConfig} to use. Whether it will be used is at
+ * the discretion of the {@link #setGpgSigner(GpgSigner)}.
+ *
+ * @param config
+ * to set; if {@code null}, the config will be loaded from the
+ * git config of the repository
+ * @return {@code this}
+ * @since 5.11
+ */
+ public CommitCommand setGpgConfig(GpgConfig config) {
+ checkCallable();
+ this.gpgConfig = config;
+ return this;
+ }
+
+ /**
* Sets a {@link CredentialsProvider}
*
* @param 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 033dd60c3b..90c1515b06 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -74,6 +74,8 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
private boolean isForceUpdate;
+ private String initialBranch;
+
/**
* Callback for status of fetch operation.
*
@@ -209,7 +211,7 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
transport.setFetchThin(thin);
configure(transport);
FetchResult result = transport.fetch(monitor,
- applyOptions(refSpecs));
+ applyOptions(refSpecs), initialBranch);
if (!repo.isBare()) {
fetchSubmodules(result);
}
@@ -488,6 +490,24 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
}
/**
+ * Set the initial branch
+ *
+ * @param branch
+ * the initial branch to check out when cloning the repository.
+ * Can be specified as ref name (<code>refs/heads/master</code>),
+ * branch name (<code>master</code>) or tag name
+ * (<code>v1.2.3</code>). The default is to use the branch
+ * pointed to by the cloned repository's HEAD and can be
+ * requested by passing {@code null} or <code>HEAD</code>.
+ * @return {@code this}
+ * @since 5.11
+ */
+ public FetchCommand setInitialBranch(String branch) {
+ this.initialBranch = branch;
+ return this;
+ }
+
+ /**
* Register a progress callback.
*
* @param callback
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
index 64314772b7..3b3e10e7b2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
- * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2021 Chris Aniszczyk <caniszczyk@gmail.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -773,6 +773,16 @@ public class Git implements AutoCloseable {
}
/**
+ * Return a command to verify signatures of tags or commits.
+ *
+ * @return a {@link VerifySignatureCommand}
+ * @since 5.11
+ */
+ public VerifySignatureCommand verifySignature() {
+ return new VerifySignatureCommand(repo);
+ }
+
+ /**
* Get repository
*
* @return the git repository this class is interacting with; see
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
index 41fcf29ed0..240290f4f9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
@@ -15,12 +15,16 @@ import java.text.MessageFormat;
import java.util.concurrent.Callable;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryBuilder;
import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.StringUtils;
import org.eclipse.jgit.util.SystemReader;
/**
@@ -38,6 +42,8 @@ public class InitCommand implements Callable<Git> {
private FS fs;
+ private String initialBranch;
+
/**
* {@inheritDoc}
* <p>
@@ -87,11 +93,16 @@ public class InitCommand implements Callable<Git> {
builder.setWorkTree(new File(dStr));
}
}
+ builder.setInitialBranch(StringUtils.isEmptyOrNull(initialBranch)
+ ? SystemReader.getInstance().getUserConfig().getString(
+ ConfigConstants.CONFIG_INIT_SECTION, null,
+ ConfigConstants.CONFIG_KEY_DEFAULT_BRANCH)
+ : initialBranch);
Repository repository = builder.build();
if (!repository.getObjectDatabase().exists())
repository.create(bare);
return new Git(repository, true);
- } catch (IOException e) {
+ } catch (IOException | ConfigInvalidException e) {
throw new JGitInternalException(e.getMessage(), e);
}
}
@@ -184,4 +195,23 @@ public class InitCommand implements Callable<Git> {
this.fs = fs;
return this;
}
+
+ /**
+ * Set the initial branch of the new repository. If not specified
+ * ({@code null} or empty), fall back to the default name (currently
+ * master).
+ *
+ * @param branch
+ * initial branch name of the new repository
+ * @return {@code this}
+ * @throws InvalidRefNameException
+ * if the branch name is not valid
+ *
+ * @since 5.11
+ */
+ public InitCommand setInitialBranch(String branch)
+ throws InvalidRefNameException {
+ this.initialBranch = branch;
+ return this;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java
index a4ca309095..0c691062f9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Christoph Brill <egore911@egore911.de> and others
+ * Copyright (C) 2011, 2020 Christoph Brill <egore911@egore911.de> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -164,7 +164,7 @@ public class LsRemoteCommand extends
refSpecs.add(new RefSpec("refs/heads/*:refs/remotes/origin/*")); //$NON-NLS-1$
Collection<Ref> refs;
Map<String, Ref> refmap = new HashMap<>();
- try (FetchConnection fc = transport.openFetch()) {
+ try (FetchConnection fc = transport.openFetch(refSpecs)) {
refs = fc.getRefs();
if (refSpecs.isEmpty())
for (Ref r : refs)
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 6678af163a..836175dcea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -67,6 +67,7 @@ import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.submodule.SubmoduleWalk.IgnoreSubmoduleMode;
@@ -1137,15 +1138,19 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private List<RevCommit> calculatePickList(RevCommit headCommit)
throws GitAPIException, NoHeadException, IOException {
- Iterable<RevCommit> commitsToUse;
- try (Git git = new Git(repo)) {
- LogCommand cmd = git.log().addRange(upstreamCommit, headCommit);
- commitsToUse = cmd.call();
- }
List<RevCommit> cherryPickList = new ArrayList<>();
- for (RevCommit commit : commitsToUse) {
- if (preserveMerges || commit.getParentCount() == 1)
- cherryPickList.add(commit);
+ try (RevWalk r = new RevWalk(repo)) {
+ r.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER, true);
+ r.sort(RevSort.COMMIT_TIME_DESC, true);
+ r.markUninteresting(r.lookupCommit(upstreamCommit));
+ r.markStart(r.lookupCommit(headCommit));
+ Iterator<RevCommit> commitsToUse = r.iterator();
+ while (commitsToUse.hasNext()) {
+ RevCommit commit = commitsToUse.next();
+ if (preserveMerges || commit.getParentCount() == 1) {
+ cherryPickList.add(commit);
+ }
+ }
}
Collections.reverse(cherryPickList);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
index 9a328a6eaa..58c18b38d1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, 2013 Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2020 Chris Aniszczyk <caniszczyk@gmail.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -18,8 +18,14 @@ import org.eclipse.jgit.api.errors.InvalidTagNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
+import org.eclipse.jgit.api.errors.ServiceUnavailableException;
+import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.GpgObjectSigner;
+import org.eclipse.jgit.lib.GpgSigner;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
@@ -31,6 +37,7 @@ import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.lib.TagBuilder;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.CredentialsProvider;
/**
* Create/update an annotated tag object or a simple unannotated tag
@@ -56,6 +63,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
* >Git documentation about Tag</a>
*/
public class TagCommand extends GitCommand<Ref> {
+
private RevObject id;
private String name;
@@ -64,11 +72,19 @@ public class TagCommand extends GitCommand<Ref> {
private PersonIdent tagger;
- private boolean signed;
+ private Boolean signed;
private boolean forceUpdate;
- private boolean annotated = true;
+ private Boolean annotated;
+
+ private String signingKey;
+
+ private GpgConfig gpgConfig;
+
+ private GpgObjectSigner gpgSigner;
+
+ private CredentialsProvider credentialsProvider;
/**
* <p>Constructor for TagCommand.</p>
@@ -77,6 +93,7 @@ public class TagCommand extends GitCommand<Ref> {
*/
protected TagCommand(Repository repo) {
super(repo);
+ this.credentialsProvider = CredentialsProvider.getDefault();
}
/**
@@ -108,10 +125,7 @@ public class TagCommand extends GitCommand<Ref> {
id = revWalk.parseCommit(objectId);
}
- if (!annotated) {
- if (message != null || tagger != null)
- throw new JGitInternalException(
- JGitText.get().messageAndTaggerNotAllowedInUnannotatedTags);
+ if (!isAnnotated()) {
return updateTagRef(id, revWalk, name,
"SimpleTag[" + name + " : " + id //$NON-NLS-1$ //$NON-NLS-2$
+ "]"); //$NON-NLS-1$
@@ -124,6 +138,11 @@ public class TagCommand extends GitCommand<Ref> {
newTag.setTagger(tagger);
newTag.setObjectId(id);
+ if (gpgSigner != null) {
+ gpgSigner.signObject(newTag, signingKey, tagger,
+ credentialsProvider, gpgConfig);
+ }
+
// write the tag object
try (ObjectInserter inserter = repo.newObjectInserter()) {
ObjectId tagId = inserter.insert(newTag);
@@ -158,9 +177,17 @@ public class TagCommand extends GitCommand<Ref> {
throw new ConcurrentRefUpdateException(
JGitText.get().couldNotLockHEAD, tagRef.getRef(),
updateResult);
+ case NO_CHANGE:
+ if (forceUpdate) {
+ return repo.exactRef(refName);
+ }
+ throw new RefAlreadyExistsException(MessageFormat
+ .format(JGitText.get().tagAlreadyExists, newTagToString),
+ updateResult);
case REJECTED:
throw new RefAlreadyExistsException(MessageFormat.format(
- JGitText.get().tagAlreadyExists, newTagToString));
+ JGitText.get().tagAlreadyExists, newTagToString),
+ updateResult);
default:
throw new JGitInternalException(MessageFormat.format(
JGitText.get().updatingRefFailed, refName, newTagToString,
@@ -177,20 +204,60 @@ public class TagCommand extends GitCommand<Ref> {
*
* @throws InvalidTagNameException
* if the tag name is null or invalid
- * @throws UnsupportedOperationException
- * if the tag is signed (not supported yet)
+ * @throws ServiceUnavailableException
+ * if the tag should be signed but no signer can be found
+ * @throws UnsupportedSigningFormatException
+ * if the tag should be signed but {@code gpg.format} is not
+ * {@link GpgFormat#OPENPGP}
*/
private void processOptions(RepositoryState state)
- throws InvalidTagNameException {
- if (tagger == null && annotated)
- tagger = new PersonIdent(repo);
- if (name == null || !Repository.isValidRefName(Constants.R_TAGS + name))
+ throws InvalidTagNameException, ServiceUnavailableException,
+ UnsupportedSigningFormatException {
+ if (name == null
+ || !Repository.isValidRefName(Constants.R_TAGS + name)) {
throw new InvalidTagNameException(
MessageFormat.format(JGitText.get().tagNameInvalid,
name == null ? "<null>" : name)); //$NON-NLS-1$
- if (signed)
- throw new UnsupportedOperationException(
- JGitText.get().signingNotSupportedOnTag);
+ }
+ if (!isAnnotated()) {
+ if ((message != null && !message.isEmpty()) || tagger != null) {
+ throw new JGitInternalException(JGitText
+ .get().messageAndTaggerNotAllowedInUnannotatedTags);
+ }
+ } else {
+ if (tagger == null) {
+ tagger = new PersonIdent(repo);
+ }
+ // Figure out whether to sign.
+ if (!(Boolean.FALSE.equals(signed) && signingKey == null)) {
+ if (gpgConfig == null) {
+ gpgConfig = new GpgConfig(repo.getConfig());
+ }
+ boolean doSign = isSigned() || gpgConfig.isSignAllTags();
+ if (!Boolean.TRUE.equals(annotated) && !doSign) {
+ doSign = gpgConfig.isSignAnnotated();
+ }
+ if (doSign) {
+ if (signingKey == null) {
+ signingKey = gpgConfig.getSigningKey();
+ }
+ if (gpgSigner == null) {
+ GpgSigner signer = GpgSigner.getDefault();
+ if (!(signer instanceof GpgObjectSigner)) {
+ throw new ServiceUnavailableException(
+ JGitText.get().signingServiceUnavailable);
+ }
+ gpgSigner = (GpgObjectSigner) signer;
+ }
+ // The message of a signed tag must end in a newline because
+ // the signature will be appended.
+ if (message != null && !message.isEmpty()
+ && !message.endsWith("\n")) { //$NON-NLS-1$
+ message += '\n';
+ }
+ }
+ }
+ }
}
/**
@@ -238,24 +305,61 @@ public class TagCommand extends GitCommand<Ref> {
}
/**
- * Whether this tag is signed
+ * Whether {@link #setSigned(boolean) setSigned(true)} has been called or
+ * whether a {@link #setSigningKey(String) signing key ID} has been set;
+ * i.e., whether -s or -u was specified explicitly.
*
* @return whether the tag is signed
*/
public boolean isSigned() {
- return signed;
+ return Boolean.TRUE.equals(signed) || signingKey != null;
}
/**
* If set to true the Tag command creates a signed tag object. This
- * corresponds to the parameter -s on the command line.
+ * corresponds to the parameter -s (--sign or --no-sign) on the command
+ * line.
+ * <p>
+ * If {@code true}, the tag will be a signed annotated tag.
+ * </p>
*
* @param signed
- * a boolean.
+ * whether to sign
* @return {@code this}
*/
public TagCommand setSigned(boolean signed) {
- this.signed = signed;
+ checkCallable();
+ this.signed = Boolean.valueOf(signed);
+ return this;
+ }
+
+ /**
+ * Sets the {@link GpgSigner} to use if the commit is to be signed.
+ *
+ * @param signer
+ * to use; if {@code null}, the default signer will be used
+ * @return {@code this}
+ * @since 5.11
+ */
+ public TagCommand setGpgSigner(GpgObjectSigner signer) {
+ checkCallable();
+ this.gpgSigner = signer;
+ return this;
+ }
+
+ /**
+ * Sets an external {@link GpgConfig} to use. Whether it will be used is at
+ * the discretion of the {@link #setGpgSigner(GpgObjectSigner)}.
+ *
+ * @param config
+ * to set; if {@code null}, the config will be loaded from the
+ * git config of the repository
+ * @return {@code this}
+ * @since 5.11
+ */
+ public TagCommand setGpgConfig(GpgConfig config) {
+ checkCallable();
+ this.gpgConfig = config;
return this;
}
@@ -268,6 +372,7 @@ public class TagCommand extends GitCommand<Ref> {
* @return {@code this}
*/
public TagCommand setTagger(PersonIdent tagger) {
+ checkCallable();
this.tagger = tagger;
return this;
}
@@ -291,14 +396,15 @@ public class TagCommand extends GitCommand<Ref> {
}
/**
- * Sets the object id of the tag. If the object id is null, the commit
- * pointed to from HEAD will be used.
+ * Sets the object id of the tag. If the object id is {@code null}, the
+ * commit pointed to from HEAD will be used.
*
* @param id
* a {@link org.eclipse.jgit.revwalk.RevObject} object.
* @return {@code this}
*/
public TagCommand setObjectId(RevObject id) {
+ checkCallable();
this.id = id;
return this;
}
@@ -321,6 +427,7 @@ public class TagCommand extends GitCommand<Ref> {
* @return {@code this}
*/
public TagCommand setForceUpdate(boolean forceUpdate) {
+ checkCallable();
this.forceUpdate = forceUpdate;
return this;
}
@@ -334,18 +441,77 @@ public class TagCommand extends GitCommand<Ref> {
* @since 3.0
*/
public TagCommand setAnnotated(boolean annotated) {
- this.annotated = annotated;
+ checkCallable();
+ this.annotated = Boolean.valueOf(annotated);
return this;
}
/**
- * Whether this will create an annotated command
+ * Whether this will create an annotated tag.
*
* @return true if this command will create an annotated tag (default is
* true)
* @since 3.0
*/
public boolean isAnnotated() {
- return annotated;
+ boolean setExplicitly = Boolean.TRUE.equals(annotated) || isSigned();
+ if (setExplicitly) {
+ return true;
+ }
+ // Annotated at default (not set explicitly)
+ return annotated == null;
}
+
+ /**
+ * Sets the signing key.
+ * <p>
+ * Per spec of {@code 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} is specified a default will be
+ * obtained from the configuration.
+ * </p>
+ * <p>
+ * If set to a non-{@code null} value, the tag will be a signed annotated
+ * tag.
+ * </p>
+ *
+ * @param signingKey
+ * signing key; {@code null} allowed
+ * @return {@code this}
+ * @since 5.11
+ */
+ public TagCommand setSigningKey(String signingKey) {
+ checkCallable();
+ this.signingKey = signingKey;
+ return this;
+ }
+
+ /**
+ * Retrieves the signing key ID.
+ *
+ * @return the key ID set, or {@code null} if none is set
+ * @since 5.11
+ */
+ public String getSigningKey() {
+ return signingKey;
+ }
+
+ /**
+ * Sets a {@link CredentialsProvider}
+ *
+ * @param credentialsProvider
+ * the provider to use when querying for credentials (eg., during
+ * signing)
+ * @return {@code this}
+ * @since 5.11
+ */
+ public TagCommand setCredentialsProvider(
+ CredentialsProvider credentialsProvider) {
+ checkCallable();
+ this.credentialsProvider = credentialsProvider;
+ return this;
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java
new file mode 100644
index 0000000000..21cddf75b7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.api;
+
+import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.revwalk.RevObject;
+
+/**
+ * A {@code VerificationResult} describes the outcome of a signature
+ * verification.
+ *
+ * @see VerifySignatureCommand
+ *
+ * @since 5.11
+ */
+public interface VerificationResult {
+
+ /**
+ * If an error occurred during signature verification, this retrieves the
+ * exception.
+ *
+ * @return the exception, or {@code null} if none occurred
+ */
+ Throwable getException();
+
+ /**
+ * Retrieves the signature verification result.
+ *
+ * @return the result, or {@code null} if none was computed
+ */
+ GpgSignatureVerifier.SignatureVerification getVerification();
+
+ /**
+ * Retrieves the git object of which the signature was verified.
+ *
+ * @return the git object
+ */
+ RevObject getObject();
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java
new file mode 100644
index 0000000000..6a2a44ea2d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.ServiceUnavailableException;
+import org.eclipse.jgit.api.errors.WrongObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+/**
+ * A command to verify GPG signatures on tags or commits.
+ *
+ * @since 5.11
+ */
+public class VerifySignatureCommand extends GitCommand<Map<String, VerificationResult>> {
+
+ /**
+ * Describes what kind of objects shall be handled by a
+ * {@link VerifySignatureCommand}.
+ */
+ public enum VerifyMode {
+ /**
+ * Handle any object type, ignore anything that is not a commit or tag.
+ */
+ ANY,
+ /**
+ * Handle only commits; throw a {@link WrongObjectTypeException} for
+ * anything else.
+ */
+ COMMITS,
+ /**
+ * Handle only tags; throw a {@link WrongObjectTypeException} for
+ * anything else.
+ */
+ TAGS
+ }
+
+ private final Set<String> namesToCheck = new HashSet<>();
+
+ private VerifyMode mode = VerifyMode.ANY;
+
+ private GpgSignatureVerifier verifier;
+
+ private GpgConfig config;
+
+ private boolean ownVerifier;
+
+ /**
+ * Creates a new {@link VerifySignatureCommand} for the given {@link Repository}.
+ *
+ * @param repo
+ * to operate on
+ */
+ public VerifySignatureCommand(Repository repo) {
+ super(repo);
+ }
+
+ /**
+ * Add a name of an object (SHA-1, ref name; anything that can be
+ * {@link Repository#resolve(String) resolved}) to the command to have its
+ * signature verified.
+ *
+ * @param name
+ * to add
+ * @return {@code this}
+ */
+ public VerifySignatureCommand addName(String name) {
+ checkCallable();
+ namesToCheck.add(name);
+ return this;
+ }
+
+ /**
+ * Add names of objects (SHA-1, ref name; anything that can be
+ * {@link Repository#resolve(String) resolved}) to the command to have their
+ * signatures verified.
+ *
+ * @param names
+ * to add; duplicates will be ignored
+ * @return {@code this}
+ */
+ public VerifySignatureCommand addNames(String... names) {
+ checkCallable();
+ namesToCheck.addAll(Arrays.asList(names));
+ return this;
+ }
+
+ /**
+ * Add names of objects (SHA-1, ref name; anything that can be
+ * {@link Repository#resolve(String) resolved}) to the command to have their
+ * signatures verified.
+ *
+ * @param names
+ * to add; duplicates will be ignored
+ * @return {@code this}
+ */
+ public VerifySignatureCommand addNames(Collection<String> names) {
+ checkCallable();
+ namesToCheck.addAll(names);
+ return this;
+ }
+
+ /**
+ * Sets the mode of operation for this command.
+ *
+ * @param mode
+ * the {@link VerifyMode} to set
+ * @return {@code this}
+ */
+ public VerifySignatureCommand setMode(@NonNull VerifyMode mode) {
+ checkCallable();
+ this.mode = mode;
+ return this;
+ }
+
+ /**
+ * Sets the {@link GpgSignatureVerifier} to use.
+ *
+ * @param verifier
+ * the {@link GpgSignatureVerifier} to use, or {@code null} to
+ * use the default verifier
+ * @return {@code this}
+ */
+ public VerifySignatureCommand setVerifier(GpgSignatureVerifier verifier) {
+ checkCallable();
+ this.verifier = verifier;
+ return this;
+ }
+
+ /**
+ * Sets an external {@link GpgConfig} to use. Whether it will be used it at
+ * the discretion of the {@link #setVerifier(GpgSignatureVerifier)}.
+ *
+ * @param config
+ * to set; if {@code null}, the config will be loaded from the
+ * git config of the repository
+ * @return {@code this}
+ * @since 5.11
+ */
+ public VerifySignatureCommand setGpgConfig(GpgConfig config) {
+ checkCallable();
+ this.config = config;
+ return this;
+ }
+
+ /**
+ * Retrieves the currently set {@link GpgSignatureVerifier}. Can be used
+ * after a successful {@link #call()} to get the verifier that was used.
+ *
+ * @return the {@link GpgSignatureVerifier}
+ */
+ public GpgSignatureVerifier getVerifier() {
+ return verifier;
+ }
+
+ /**
+ * {@link Repository#resolve(String) Resolves} all names added to the
+ * command to git objects and verifies their signature. Non-existing objects
+ * are ignored.
+ * <p>
+ * Depending on the {@link #setMode(VerifyMode)}, only tags or commits or
+ * any kind of objects are allowed.
+ * </p>
+ * <p>
+ * Unsigned objects are silently skipped.
+ * </p>
+ *
+ * @return a map of the given names to the corresponding
+ * {@link VerificationResult}, excluding ignored or skipped objects.
+ * @throws ServiceUnavailableException
+ * if no {@link GpgSignatureVerifier} was set and no
+ * {@link GpgSignatureVerifierFactory} is available
+ * @throws WrongObjectTypeException
+ * if a name resolves to an object of a type not allowed by the
+ * {@link #setMode(VerifyMode)} mode
+ */
+ @Override
+ @NonNull
+ public Map<String, VerificationResult> call()
+ throws ServiceUnavailableException, WrongObjectTypeException {
+ checkCallable();
+ setCallable(false);
+ Map<String, VerificationResult> result = new HashMap<>();
+ if (verifier == null) {
+ GpgSignatureVerifierFactory factory = GpgSignatureVerifierFactory
+ .getDefault();
+ if (factory == null) {
+ throw new ServiceUnavailableException(
+ JGitText.get().signatureVerificationUnavailable);
+ }
+ verifier = factory.getVerifier();
+ ownVerifier = true;
+ }
+ if (config == null) {
+ config = new GpgConfig(repo.getConfig());
+ }
+ try (RevWalk walk = new RevWalk(repo)) {
+ for (String toCheck : namesToCheck) {
+ ObjectId id = repo.resolve(toCheck);
+ if (id != null && !ObjectId.zeroId().equals(id)) {
+ RevObject object;
+ try {
+ object = walk.parseAny(id);
+ } catch (MissingObjectException e) {
+ continue;
+ }
+ VerificationResult verification = verifyOne(object);
+ if (verification != null) {
+ result.put(toCheck, verification);
+ }
+ }
+ }
+ } catch (IOException e) {
+ throw new JGitInternalException(
+ JGitText.get().signatureVerificationError, e);
+ } finally {
+ if (ownVerifier) {
+ verifier.clear();
+ }
+ }
+ return result;
+ }
+
+ private VerificationResult verifyOne(RevObject object)
+ throws WrongObjectTypeException, IOException {
+ int type = object.getType();
+ if (VerifyMode.TAGS.equals(mode) && type != Constants.OBJ_TAG) {
+ throw new WrongObjectTypeException(object, Constants.OBJ_TAG);
+ } else if (VerifyMode.COMMITS.equals(mode)
+ && type != Constants.OBJ_COMMIT) {
+ throw new WrongObjectTypeException(object, Constants.OBJ_COMMIT);
+ }
+ if (type == Constants.OBJ_COMMIT || type == Constants.OBJ_TAG) {
+ try {
+ GpgSignatureVerifier.SignatureVerification verification = verifier
+ .verifySignature(object, config);
+ if (verification == null) {
+ // Not signed
+ return null;
+ }
+ // Create new result
+ return new Result(object, verification, null);
+ } catch (JGitInternalException e) {
+ return new Result(object, null, e);
+ }
+ }
+ return null;
+ }
+
+ private static class Result implements VerificationResult {
+
+ private final Throwable throwable;
+
+ private final SignatureVerification verification;
+
+ private final RevObject object;
+
+ public Result(RevObject object, SignatureVerification verification,
+ Throwable throwable) {
+ this.object = object;
+ this.verification = verification;
+ this.throwable = throwable;
+ }
+
+ @Override
+ public Throwable getException() {
+ return throwable;
+ }
+
+ @Override
+ public SignatureVerification getVerification() {
+ return verification;
+ }
+
+ @Override
+ public RevObject getObject() {
+ return object;
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefAlreadyExistsException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefAlreadyExistsException.java
index 7e39361eff..81b7bd84cb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefAlreadyExistsException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefAlreadyExistsException.java
@@ -1,42 +1,17 @@
/*
- * Copyright (C) 2010,Mathias Kinzler <mathias.kinzler@sap.com> and
- * other copyright owners as documented in the project's IP log.
+ * Copyright (C) 2010, 2020 Mathias Kinzler <mathias.kinzler@sap.com> and others
*
* 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
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://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.
+ * SPDX-License-Identifier: BSD-3-Clause
*/
package org.eclipse.jgit.api.errors;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.RefUpdate;
+
/**
* Thrown when trying to create a {@link org.eclipse.jgit.lib.Ref} with the same
* name as an existing one
@@ -44,13 +19,43 @@ package org.eclipse.jgit.api.errors;
public class RefAlreadyExistsException extends GitAPIException {
private static final long serialVersionUID = 1L;
+ private final RefUpdate.Result updateResult;
+
/**
- * Constructor for RefAlreadyExistsException
+ * Creates a new instance with the given message.
*
* @param message
* error message
*/
public RefAlreadyExistsException(String message) {
+ this(message, null);
+ }
+
+ /**
+ * Constructor for RefAlreadyExistsException
+ *
+ * @param message
+ * error message
+ * @param updateResult
+ * that caused the exception; may be {@code null}
+ * @since 5.11
+ */
+ public RefAlreadyExistsException(String message,
+ @Nullable RefUpdate.Result updateResult) {
super(message);
+ this.updateResult = updateResult;
+ }
+
+ /**
+ * Retrieves the {@link org.eclipse.jgit.lib.RefUpdate.Result
+ * RefUpdate.Result} that caused the exception.
+ *
+ * @return the {@link org.eclipse.jgit.lib.RefUpdate.Result
+ * RefUpdate.Result} or {@code null} if unknown
+ * @since 5.11
+ */
+ @Nullable
+ public RefUpdate.Result getUpdateResult() {
+ return updateResult;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/WrongObjectTypeException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/WrongObjectTypeException.java
new file mode 100644
index 0000000000..f639c2f838
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/WrongObjectTypeException.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.api.errors;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * A given object is not of an expected object type.
+ *
+ * @since 5.11
+ */
+public class WrongObjectTypeException extends GitAPIException {
+
+ private static final long serialVersionUID = 1L;
+
+ private String name;
+
+ private int type;
+
+ /**
+ * Construct a {@link WrongObjectTypeException} for the specified object id,
+ * giving the expected type.
+ *
+ * @param id
+ * {@link ObjectId} of the object with the unexpected type
+ * @param type
+ * expected object type code; see
+ * {@link Constants}{@code .OBJ_*}.
+ */
+ public WrongObjectTypeException(ObjectId id, int type) {
+ super(MessageFormat.format(JGitText.get().objectIsNotA, id.name(),
+ Constants.typeString(type)));
+ this.name = id.name();
+ this.type = type;
+ }
+
+ /**
+ * Retrieves the name (SHA-1) of the object.
+ *
+ * @return the name
+ */
+ public String getObjectId() {
+ return name;
+ }
+
+ /**
+ * Retrieves the expected type code. See {@link Constants}{@code .OBJ_*}.
+ *
+ * @return the type code
+ */
+ public int getExpectedType() {
+ return type;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandRegistry.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandRegistry.java
index 2698e23035..1c9e9d7f71 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandRegistry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandRegistry.java
@@ -12,6 +12,7 @@ package org.eclipse.jgit.attributes;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -23,7 +24,7 @@ import org.eclipse.jgit.lib.Repository;
* @since 4.6
*/
public class FilterCommandRegistry {
- private static ConcurrentHashMap<String, FilterCommandFactory> filterCommandRegistry = new ConcurrentHashMap<>();
+ private static Map<String, FilterCommandFactory> filterCommandRegistry = new ConcurrentHashMap<>();
/**
* Register a {@link org.eclipse.jgit.attributes.FilterCommandFactory}
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 8c51a7ac2f..671475ed47 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -946,12 +946,14 @@ 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 (initialCheckout)
+ if (initialCheckout || force) {
update(name, mId, mMode);
- else
+ } else {
keep(name, dce, f);
- } else
+ }
+ } else {
conflict(name, dce, h, m);
+ }
}
} else {
// Something in Index
@@ -1214,8 +1216,12 @@ public class DirCacheCheckout {
private void keep(String path, DirCacheEntry e, WorkingTreeIterator f)
throws IOException {
- if (e != null && !FileMode.TREE.equals(e.getFileMode()))
+ if (e == null) {
+ return;
+ }
+ if (!FileMode.TREE.equals(e.getFileMode())) {
builder.add(e);
+ }
if (force) {
if (f == null || f.isModified(e, true, walk.getObjectReader())) {
kept.add(path);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoPackSignatureException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoPackSignatureException.java
index c3b1df9928..a37b8bee24 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoPackSignatureException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoPackSignatureException.java
@@ -13,8 +13,7 @@ package org.eclipse.jgit.errors;
import java.io.IOException;
/**
- * Thrown when a PackFile is found not to contain the pack signature defined by
- * git.
+ * Thrown when a Pack is found not to contain the pack signature defined by git.
*
* @since 4.5
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java
index c484984f82..1fd80867b9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java
@@ -17,7 +17,7 @@ import java.text.MessageFormat;
import org.eclipse.jgit.internal.JGitText;
/**
- * Thrown when a PackFile previously failed and is known to be unusable
+ * Thrown when a Pack previously failed and is known to be unusable
*/
public class PackInvalidException extends IOException {
private static final long serialVersionUID = 1L;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackMismatchException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackMismatchException.java
index ad5664ceb2..44b8e0193c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackMismatchException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackMismatchException.java
@@ -13,7 +13,7 @@ package org.eclipse.jgit.errors;
import java.io.IOException;
/**
- * Thrown when a PackFile no longer matches the PackIndex.
+ * Thrown when a Pack no longer matches the PackIndex.
*/
public class PackMismatchException extends IOException {
private static final long serialVersionUID = 1L;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/UnsupportedPackVersionException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/UnsupportedPackVersionException.java
index 7538229950..07aa7564dd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/UnsupportedPackVersionException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/UnsupportedPackVersionException.java
@@ -16,7 +16,7 @@ import java.text.MessageFormat;
import org.eclipse.jgit.internal.JGitText;
/**
- * Thrown when a PackFile uses a pack version not supported by JGit.
+ * Thrown when a Pack uses a pack version not supported by JGit.
*
* @since 4.5
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
index 32c3a1d28f..476c37c1c3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
@@ -11,15 +11,15 @@
package org.eclipse.jgit.events;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Manages a thread-safe list of {@link org.eclipse.jgit.events.RepositoryListener}s.
*/
public class ListenerList {
- private final ConcurrentMap<Class<? extends RepositoryListener>, CopyOnWriteArrayList<ListenerHandle>> lists = new ConcurrentHashMap<>();
+ private final Map<Class<? extends RepositoryListener>, CopyOnWriteArrayList<ListenerHandle>> lists = new ConcurrentHashMap<>();
/**
* Register a {@link org.eclipse.jgit.events.WorkingTreeModifiedListener}.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
index 4059b16b39..ce3ad2239a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
@@ -9,12 +9,10 @@
*/
package org.eclipse.jgit.hooks;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.PrintStream;
-import java.io.UnsupportedEncodingException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
import java.util.concurrent.Callable;
import org.eclipse.jgit.api.errors.AbortedByHookException;
@@ -35,21 +33,21 @@ import org.eclipse.jgit.util.io.TeeOutputStream;
* the return type which is expected from {@link #call()}
* @see <a href="http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">Git
* Hooks on the git-scm official site</a>
- * @since 4.0
+ * @since 5.11
*/
-abstract class GitHook<T> implements Callable<T> {
+public abstract class GitHook<T> implements Callable<T> {
private final Repository repo;
/**
* The output stream to be used by the hook.
*/
- protected final PrintStream outputStream;
+ private final OutputStream outputStream;
/**
* The error stream to be used by the hook.
*/
- protected final PrintStream errorStream;
+ private final OutputStream errorStream;
/**
* Constructor for GitHook.
@@ -63,7 +61,7 @@ abstract class GitHook<T> implements Callable<T> {
* The output stream the hook must use. {@code null} is allowed,
* in which case the hook will use {@code System.out}.
*/
- protected GitHook(Repository repo, PrintStream outputStream) {
+ protected GitHook(Repository repo, OutputStream outputStream) {
this(repo, outputStream, null);
}
@@ -79,8 +77,8 @@ abstract class GitHook<T> implements Callable<T> {
* The error stream the hook must use. {@code null} is allowed,
* in which case the hook will use {@code System.err}.
*/
- protected GitHook(Repository repo, PrintStream outputStream,
- PrintStream errorStream) {
+ protected GitHook(Repository repo, OutputStream outputStream,
+ OutputStream errorStream) {
this.repo = repo;
this.outputStream = outputStream;
this.errorStream = errorStream;
@@ -137,7 +135,7 @@ abstract class GitHook<T> implements Callable<T> {
* @return The output stream the hook must use. Never {@code null},
* {@code System.out} is returned by default.
*/
- protected PrintStream getOutputStream() {
+ protected OutputStream getOutputStream() {
return outputStream == null ? System.out : outputStream;
}
@@ -147,7 +145,7 @@ abstract class GitHook<T> implements Callable<T> {
* @return The error stream the hook must use. Never {@code null},
* {@code System.err} is returned by default.
*/
- protected PrintStream getErrorStream() {
+ protected OutputStream getErrorStream() {
return errorStream == null ? System.err : errorStream;
}
@@ -156,34 +154,48 @@ abstract class GitHook<T> implements Callable<T> {
*
* @throws org.eclipse.jgit.api.errors.AbortedByHookException
* If the underlying hook script exited with non-zero.
+ * @throws IOException
+ * if an IO error occurred
*/
- protected void doRun() throws AbortedByHookException {
+ protected void doRun() throws AbortedByHookException, IOException {
final ByteArrayOutputStream errorByteArray = new ByteArrayOutputStream();
final TeeOutputStream stderrStream = new TeeOutputStream(errorByteArray,
getErrorStream());
- PrintStream hookErrRedirect = null;
- try {
- hookErrRedirect = new PrintStream(stderrStream, false,
- UTF_8.name());
- } catch (UnsupportedEncodingException e) {
- // UTF-8 is guaranteed to be available
- }
Repository repository = getRepository();
FS fs = repository.getFS();
if (fs == null) {
fs = FS.DETECTED;
}
ProcessResult result = fs.runHookIfPresent(repository, getHookName(),
- getParameters(), getOutputStream(), hookErrRedirect,
+ getParameters(), getOutputStream(), stderrStream,
getStdinArgs());
if (result.isExecutedWithError()) {
- throw new AbortedByHookException(
- new String(errorByteArray.toByteArray(), UTF_8),
- getHookName(), result.getExitCode());
+ handleError(new String(errorByteArray.toByteArray(),
+ Charset.defaultCharset().name()), result);
}
}
/**
+ * Process that the hook exited with an error. This default implementation
+ * throws an {@link AbortedByHookException }. Hooks which need a different
+ * behavior can overwrite this method.
+ *
+ * @param message
+ * error message
+ * @param result
+ * The process result of the hook
+ * @throws AbortedByHookException
+ * When the hook should be aborted
+ * @since 5.11
+ */
+ protected void handleError(String message,
+ final ProcessResult result)
+ throws AbortedByHookException {
+ throw new AbortedByHookException(message, getHookName(),
+ result.getExitCode());
+ }
+
+ /**
* Check whether a 'native' (i.e. script) hook is installed in the
* repository.
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java
index 0b61ebea3f..b9dafcca31 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java
@@ -14,6 +14,7 @@ import java.io.PrintStream;
import org.eclipse.jgit.api.errors.AbortedByHookException;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.ProcessResult;
/**
* The <code>post-commit</code> hook implementation. This hook is run after the
@@ -73,4 +74,16 @@ public class PostCommitHook extends GitHook<Void> {
return NAME;
}
+
+ /**
+ * Overwrites the default implementation to never throw an
+ * {@link AbortedByHookException}, as the commit has already been done and
+ * the exit code of the post-commit hook has no effect.
+ */
+ @Override
+ protected void handleError(String message, ProcessResult result)
+ throws AbortedByHookException {
+ // Do nothing as the exit code of the post-commit hook has no effect.
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
index d7e4f79d26..9dd565ff0a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> and others
+ * Copyright (C) 2014, 2021 Andrey Loskutov <loskutov@gmx.de> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -14,8 +14,11 @@ import static org.eclipse.jgit.ignore.internal.Strings.isDirectoryPattern;
import static org.eclipse.jgit.ignore.internal.Strings.stripTrailing;
import static org.eclipse.jgit.ignore.internal.Strings.stripTrailingWhitespace;
+import java.text.MessageFormat;
+
import org.eclipse.jgit.errors.InvalidPatternException;
import org.eclipse.jgit.ignore.internal.PathMatcher;
+import org.eclipse.jgit.internal.JGitText;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -36,11 +39,11 @@ public class FastIgnoreRule {
*/
public static final char PATH_SEPARATOR = '/';
- private final IMatcher matcher;
+ private IMatcher matcher;
- private final boolean inverse;
+ private boolean inverse;
- private final boolean dirOnly;
+ private boolean dirOnly;
/**
* Constructor for FastIgnoreRule
@@ -52,8 +55,23 @@ public class FastIgnoreRule {
* (comment), this rule doesn't match anything.
*/
public FastIgnoreRule(String pattern) {
- if (pattern == null)
+ this();
+ try {
+ parse(pattern);
+ } catch (InvalidPatternException e) {
+ LOG.error(MessageFormat.format(JGitText.get().badIgnorePattern,
+ e.getPattern()), e);
+ }
+ }
+
+ FastIgnoreRule() {
+ matcher = IMatcher.NO_MATCH;
+ }
+
+ void parse(String pattern) throws InvalidPatternException {
+ if (pattern == null) {
throw new IllegalArgumentException("Pattern must not be null!"); //$NON-NLS-1$
+ }
if (pattern.length() == 0) {
dirOnly = false;
inverse = false;
@@ -90,15 +108,8 @@ public class FastIgnoreRule {
return;
}
}
- IMatcher m;
- try {
- m = PathMatcher.createPathMatcher(pattern,
- Character.valueOf(PATH_SEPARATOR), dirOnly);
- } catch (InvalidPatternException e) {
- m = NO_MATCH;
- LOG.error(e.getMessage(), e);
- }
- this.matcher = m;
+ this.matcher = PathMatcher.createPathMatcher(pattern,
+ Character.valueOf(PATH_SEPARATOR), dirOnly);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
index 0bc6124912..4e7f126a60 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Red Hat Inc. and others
+ * Copyright (C) 2010, 2021 Red Hat Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -15,16 +15,26 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.InvalidPatternException;
+import org.eclipse.jgit.internal.JGitText;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
/**
* Represents a bundle of ignore rules inherited from a base directory.
*
* This class is not thread safe, it maintains state about the last match.
*/
public class IgnoreNode {
+
+ private static final Logger LOG = LoggerFactory.getLogger(IgnoreNode.class);
+
/** Result from {@link IgnoreNode#isIgnored(String, boolean)}. */
public enum MatchResult {
/** The file is not ignored, due to a rule saying its not ignored. */
@@ -52,7 +62,7 @@ public class IgnoreNode {
* Create an empty ignore node with no rules.
*/
public IgnoreNode() {
- rules = new ArrayList<>();
+ this(new ArrayList<>());
}
/**
@@ -75,15 +85,47 @@ public class IgnoreNode {
* Error thrown when reading an ignore file.
*/
public void parse(InputStream in) throws IOException {
+ parse(null, in);
+ }
+
+ /**
+ * Parse files according to gitignore standards.
+ *
+ * @param sourceName
+ * identifying the source of the stream
+ * @param in
+ * input stream holding the standard ignore format. The caller is
+ * responsible for closing the stream.
+ * @throws java.io.IOException
+ * Error thrown when reading an ignore file.
+ * @since 5.11
+ */
+ public void parse(String sourceName, InputStream in) throws IOException {
BufferedReader br = asReader(in);
String txt;
+ int lineNumber = 1;
while ((txt = br.readLine()) != null) {
if (txt.length() > 0 && !txt.startsWith("#") && !txt.equals("/")) { //$NON-NLS-1$ //$NON-NLS-2$
- FastIgnoreRule rule = new FastIgnoreRule(txt);
+ FastIgnoreRule rule = new FastIgnoreRule();
+ try {
+ rule.parse(txt);
+ } catch (InvalidPatternException e) {
+ if (sourceName != null) {
+ LOG.error(MessageFormat.format(
+ JGitText.get().badIgnorePatternFull, sourceName,
+ Integer.toString(lineNumber), e.getPattern(),
+ e.getLocalizedMessage()), e);
+ } else {
+ LOG.error(MessageFormat.format(
+ JGitText.get().badIgnorePattern,
+ e.getPattern()), e);
+ }
+ }
if (!rule.isEmpty()) {
rules.add(rule);
}
}
+ lineNumber++;
}
}
@@ -135,7 +177,8 @@ public class IgnoreNode {
* undetermined
* @since 4.11
*/
- public Boolean checkIgnored(String entryPath, boolean isDirectory) {
+ public @Nullable Boolean checkIgnored(String entryPath,
+ boolean isDirectory) {
// Parse rules in the reverse order that they were read because later
// rules have higher priority
for (int i = rules.size() - 1; i > -1; i--) {
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 892657d5d3..9d215ca455 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com>
- * Copyright (C) 2012, Research In Motion Limited and others
+ * Copyright (C) 2012, 2021 Research In Motion Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -58,6 +58,8 @@ public class JGitText extends TranslationBundle {
/***/ public String badEntryName;
/***/ public String badEscape;
/***/ public String badGroupHeader;
+ /***/ public String badIgnorePattern;
+ /***/ public String badIgnorePatternFull;
/***/ public String badObjectType;
/***/ public String badRef;
/***/ public String badSectionEntry;
@@ -261,6 +263,7 @@ public class JGitText extends TranslationBundle {
/***/ public String downloadCancelledDuringIndexing;
/***/ public String duplicateAdvertisementsOf;
/***/ public String duplicateRef;
+ /***/ public String duplicateRefAttribute;
/***/ public String duplicateRemoteRefUpdateIsIllegal;
/***/ public String duplicateStagesNotAllowed;
/***/ public String eitherGitDirOrWorkTreeRequired;
@@ -338,6 +341,10 @@ public class JGitText extends TranslationBundle {
/***/ public String hoursAgo;
/***/ public String httpConfigCannotNormalizeURL;
/***/ public String httpConfigInvalidURL;
+ /***/ public String httpFactoryInUse;
+ /***/ public String httpPreAuthTooLate;
+ /***/ public String httpUserInfoDecodeError;
+ /***/ public String httpWrongConnectionType;
/***/ public String hugeIndexesAreNotSupportedByJgitYet;
/***/ public String hunkBelongsToAnotherFile;
/***/ public String hunkDisconnectedFromFile;
@@ -591,6 +598,7 @@ public class JGitText extends TranslationBundle {
/***/ public String reftableDirExists;
/***/ public String reftableRecordsMustIncrease;
/***/ public String refUpdateReturnCodeWas;
+ /***/ public String remoteBranchNotFound;
/***/ public String remoteConfigHasNoURIAssociated;
/***/ public String remoteDoesNotHaveSpec;
/***/ public String remoteDoesNotSupportSmartHTTPPush;
@@ -645,7 +653,9 @@ public class JGitText extends TranslationBundle {
/***/ public String shortReadOfBlock;
/***/ public String shortReadOfOptionalDIRCExtensionExpectedAnotherBytes;
/***/ public String shortSkipOfBlock;
- /***/ public String signingNotSupportedOnTag;
+ /***/ public String signatureVerificationError;
+ /***/ public String signatureVerificationUnavailable;
+ /***/ public String signedTagMessageNoLf;
/***/ public String signingServiceUnavailable;
/***/ public String similarityScoreMustBeWithinBounds;
/***/ public String skipMustBeNonNegative;
@@ -786,6 +796,13 @@ public class JGitText extends TranslationBundle {
/***/ public String URINotSupported;
/***/ public String userConfigInvalid;
/***/ public String validatingGitModules;
+ /***/ public String verifySignatureBad;
+ /***/ public String verifySignatureExpired;
+ /***/ public String verifySignatureGood;
+ /***/ public String verifySignatureIssuer;
+ /***/ public String verifySignatureKey;
+ /***/ public String verifySignatureMade;
+ /***/ public String verifySignatureTrust;
/***/ public String walkFailure;
/***/ public String wantNoSpaceWithCapabilities;
/***/ public String wantNotValid;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ElectionRound.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ElectionRound.java
deleted file mode 100644
index 5ddbcbd0ed..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ElectionRound.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.eclipse.jgit.internal.ketch.KetchConstants.TERM;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TreeFormatter;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.util.time.ProposedTimestamp;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The initial {@link Round} for a leaderless repository, used to establish a
- * leader.
- */
-class ElectionRound extends Round {
- private static final Logger log = LoggerFactory.getLogger(ElectionRound.class);
-
- private long term;
-
- ElectionRound(KetchLeader leader, LogIndex head) {
- super(leader, head);
- }
-
- @Override
- void start() throws IOException {
- ObjectId id;
- try (Repository git = leader.openRepository();
- ProposedTimestamp ts = getSystem().getClock().propose();
- ObjectInserter inserter = git.newObjectInserter()) {
- id = bumpTerm(git, ts, inserter);
- inserter.flush();
- blockUntil(ts);
- }
- runAsync(id);
- }
-
- @Override
- void success() {
- // Do nothing upon election, KetchLeader will copy the term.
- }
-
- long getTerm() {
- return term;
- }
-
- private ObjectId bumpTerm(Repository git, ProposedTimestamp ts,
- ObjectInserter inserter) throws IOException {
- CommitBuilder b = new CommitBuilder();
- if (!ObjectId.zeroId().equals(acceptedOldIndex)) {
- try (RevWalk rw = new RevWalk(git)) {
- RevCommit c = rw.parseCommit(acceptedOldIndex);
- if (getSystem().requireMonotonicLeaderElections()) {
- if (ts.read(SECONDS) < c.getCommitTime()) {
- throw new TimeIsUncertainException();
- }
- }
- b.setTreeId(c.getTree());
- b.setParentId(acceptedOldIndex);
- term = parseTerm(c.getFooterLines(TERM)) + 1;
- }
- } else {
- term = 1;
- b.setTreeId(inserter.insert(new TreeFormatter()));
- }
-
- StringBuilder msg = new StringBuilder();
- msg.append(KetchConstants.TERM.getName())
- .append(": ") //$NON-NLS-1$
- .append(term);
-
- String tag = leader.getSystem().newLeaderTag();
- if (tag != null && !tag.isEmpty()) {
- msg.append(' ').append(tag);
- }
-
- b.setAuthor(leader.getSystem().newCommitter(ts));
- b.setCommitter(b.getAuthor());
- b.setMessage(msg.toString());
-
- if (log.isDebugEnabled()) {
- log.debug("Trying to elect myself " + b.getMessage()); //$NON-NLS-1$
- }
- return inserter.insert(b);
- }
-
- private static long parseTerm(List<String> footer) {
- if (footer.isEmpty()) {
- return 0;
- }
-
- String s = footer.get(0);
- int p = s.indexOf(' ');
- if (p > 0) {
- s = s.substring(0, p);
- }
- return Long.parseLong(s, 10);
- }
-
- private void blockUntil(ProposedTimestamp ts) throws IOException {
- try {
- ts.blockUntil(getSystem().getMaxWaitForMonotonicClock());
- } catch (InterruptedException | TimeoutException e) {
- throw new TimeIsUncertainException(e);
- }
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchConstants.java
deleted file mode 100644
index f4a7f592f0..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchConstants.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import org.eclipse.jgit.revwalk.FooterKey;
-
-/**
- * Frequently used constants in a Ketch system.
- */
-public class KetchConstants {
- /**
- * Default reference namespace holding {@link #ACCEPTED} and
- * {@link #COMMITTED} references and the {@link #STAGE} sub-namespace.
- */
- public static final String DEFAULT_TXN_NAMESPACE = "refs/txn/"; //$NON-NLS-1$
-
- /** Reference name holding the RefTree accepted by a follower. */
- public static final String ACCEPTED = "accepted"; //$NON-NLS-1$
-
- /** Reference name holding the RefTree known to be committed. */
- public static final String COMMITTED = "committed"; //$NON-NLS-1$
-
- /** Reference subdirectory holding proposed heads. */
- public static final String STAGE = "stage/"; //$NON-NLS-1$
-
- /** Footer containing the current term. */
- public static final FooterKey TERM = new FooterKey("Term"); //$NON-NLS-1$
-
- /** Section for Ketch configuration ({@code ketch}). */
- public static final String CONFIG_SECTION_KETCH = "ketch"; //$NON-NLS-1$
-
- /** Behavior for a replica ({@code remote.$name.ketch-type}) */
- public static final String CONFIG_KEY_TYPE = "ketch-type"; //$NON-NLS-1$
-
- /** Behavior for a replica ({@code remote.$name.ketch-commit}) */
- public static final String CONFIG_KEY_COMMIT = "ketch-commit"; //$NON-NLS-1$
-
- /** Behavior for a replica ({@code remote.$name.ketch-speed}) */
- public static final String CONFIG_KEY_SPEED = "ketch-speed"; //$NON-NLS-1$
-
- private KetchConstants() {
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java
deleted file mode 100644
index 743d1939c8..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java
+++ /dev/null
@@ -1,604 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static org.eclipse.jgit.internal.ketch.KetchLeader.State.CANDIDATE;
-import static org.eclipse.jgit.internal.ketch.KetchLeader.State.LEADER;
-import static org.eclipse.jgit.internal.ketch.KetchLeader.State.SHUTDOWN;
-import static org.eclipse.jgit.internal.ketch.KetchReplica.Participation.FOLLOWER_ONLY;
-import static org.eclipse.jgit.internal.ketch.Proposal.State.QUEUED;
-
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-import org.eclipse.jgit.internal.storage.reftree.RefTree;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A leader managing consensus across remote followers.
- * <p>
- * A leader instance starts up in
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader.State#CANDIDATE} and tries
- * to begin a new term by sending an
- * {@link org.eclipse.jgit.internal.ketch.ElectionRound} to all replicas. Its
- * term starts if a majority of replicas have accepted this leader instance for
- * the term.
- * <p>
- * Once elected by a majority the instance enters
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader.State#LEADER} and runs
- * proposals offered to {@link #queueProposal(Proposal)}. This continues until
- * the leader is timed out for inactivity, or is deposed by a competing leader
- * gaining its own majority.
- * <p>
- * Once timed out or deposed this {@code KetchLeader} instance should be
- * discarded, and a new instance takes over.
- * <p>
- * Each leader instance coordinates a group of
- * {@link org.eclipse.jgit.internal.ketch.KetchReplica}s. Replica instances are
- * owned by the leader instance and must be discarded when the leader is
- * discarded.
- * <p>
- * In Ketch all push requests are issued through the leader. The steps are as
- * follows (see {@link org.eclipse.jgit.internal.ketch.KetchPreReceive} for an
- * example):
- * <ul>
- * <li>Create a {@link org.eclipse.jgit.internal.ketch.Proposal} with the
- * {@link org.eclipse.jgit.transport.ReceiveCommand}s that represent the push.
- * <li>Invoke {@link #queueProposal(Proposal)} on the leader instance.
- * <li>Wait for consensus with
- * {@link org.eclipse.jgit.internal.ketch.Proposal#await()}.
- * <li>To examine the status of the push, check
- * {@link org.eclipse.jgit.internal.ketch.Proposal#getCommands()}, looking at
- * {@link org.eclipse.jgit.internal.storage.reftree.Command#getResult()}.
- * </ul>
- * <p>
- * The leader gains consensus by first pushing the needed objects and a
- * {@link org.eclipse.jgit.internal.storage.reftree.RefTree} representing the
- * desired target repository state to the {@code refs/txn/accepted} branch on
- * each of the replicas. Once a majority has succeeded, the leader commits the
- * state by either pushing the {@code refs/txn/accepted} value to
- * {@code refs/txn/committed} (for Ketch-aware replicas) or by pushing updates
- * to {@code refs/heads/master}, etc. for stock Git replicas.
- * <p>
- * Internally, the actual transport to replicas is performed on background
- * threads via the {@link org.eclipse.jgit.internal.ketch.KetchSystem}'s
- * executor service. For performance, the
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader},
- * {@link org.eclipse.jgit.internal.ketch.KetchReplica} and
- * {@link org.eclipse.jgit.internal.ketch.Proposal} objects share some state,
- * and may invoke each other's methods on different threads. This access is
- * protected by the leader's {@link #lock} object. Care must be taken to prevent
- * concurrent access by correctly obtaining the leader's lock.
- */
-public abstract class KetchLeader {
- private static final Logger log = LoggerFactory.getLogger(KetchLeader.class);
-
- /** Current state of the leader instance. */
- public enum State {
- /** Newly created instance trying to elect itself leader. */
- CANDIDATE,
-
- /** Leader instance elected by a majority. */
- LEADER,
-
- /** Instance has been deposed by another with a more recent term. */
- DEPOSED,
-
- /** Leader has been gracefully shutdown, e.g. due to inactivity. */
- SHUTDOWN;
- }
-
- private final KetchSystem system;
-
- /** Leader's knowledge of replicas for this repository. */
- private KetchReplica[] voters;
- private KetchReplica[] followers;
- private LocalReplica self;
-
- /**
- * Lock protecting all data within this leader instance.
- * <p>
- * This lock extends into the {@link KetchReplica} instances used by the
- * leader. They share the same lock instance to simplify concurrency.
- */
- final Lock lock;
-
- private State state = CANDIDATE;
-
- /** Term of this leader, once elected. */
- private long term;
-
- /**
- * Pending proposals accepted into the queue in FIFO order.
- * <p>
- * These proposals were preflighted and do not contain any conflicts with
- * each other and their expectations matched the leader's local view of the
- * agreed upon {@code refs/txn/accepted} tree.
- */
- private final List<Proposal> queued;
-
- /**
- * State of the repository's RefTree after applying all entries in
- * {@link #queued}. New proposals must be consistent with this tree to be
- * appended to the end of {@link #queued}.
- * <p>
- * Must be deep-copied with {@link RefTree#copy()} if
- * {@link #roundHoldsReferenceToRefTree} is {@code true}.
- */
- private RefTree refTree;
-
- /**
- * If {@code true} {@link #refTree} must be duplicated before queuing the
- * next proposal. The {@link #refTree} was passed into the constructor of a
- * {@link ProposalRound}, and that external reference to the {@link RefTree}
- * object is held by the proposal until it materializes the tree object in
- * the object store. This field is set {@code true} when the proposal begins
- * execution and set {@code false} once tree objects are persisted in the
- * local repository's object store or {@link #refTree} is replaced with a
- * copy to isolate it from any running rounds.
- * <p>
- * If proposals arrive less frequently than the {@code RefTree} is written
- * out to the repository the {@link #roundHoldsReferenceToRefTree} behavior
- * avoids duplicating {@link #refTree}, reducing both time and memory used.
- * However if proposals arrive more frequently {@link #refTree} must be
- * duplicated to prevent newly queued proposals from corrupting the
- * {@link #runningRound}.
- */
- volatile boolean roundHoldsReferenceToRefTree;
-
- /** End of the leader's log. */
- private LogIndex headIndex;
-
- /** Leader knows this (and all prior) states are committed. */
- private LogIndex committedIndex;
-
- /**
- * Is the leader idle with no work pending? If {@code true} there is no work
- * for the leader (normal state). This field is {@code false} when the
- * leader thread is scheduled for execution, or while {@link #runningRound}
- * defines a round in progress.
- */
- private boolean idle;
-
- /** Current round the leader is preparing and waiting for a vote on. */
- private Round runningRound;
-
- /**
- * Construct a leader for a Ketch instance.
- *
- * @param system
- * Ketch system configuration the leader must adhere to.
- */
- protected KetchLeader(KetchSystem system) {
- this.system = system;
- this.lock = new ReentrantLock(true /* fair */);
- this.queued = new ArrayList<>(4);
- this.idle = true;
- }
-
- /** @return system configuration. */
- KetchSystem getSystem() {
- return system;
- }
-
- /**
- * Configure the replicas used by this Ketch instance.
- * <p>
- * Replicas should be configured once at creation before any proposals are
- * executed. Once elections happen, <b>reconfiguration is a complicated
- * concept that is not currently supported</b>.
- *
- * @param replicas
- * members participating with the same repository.
- */
- public void setReplicas(Collection<KetchReplica> replicas) {
- List<KetchReplica> v = new ArrayList<>(5);
- List<KetchReplica> f = new ArrayList<>(5);
- for (KetchReplica r : replicas) {
- switch (r.getParticipation()) {
- case FULL:
- v.add(r);
- break;
-
- case FOLLOWER_ONLY:
- f.add(r);
- break;
- }
- }
-
- Collection<Integer> validVoters = validVoterCounts();
- if (!validVoters.contains(Integer.valueOf(v.size()))) {
- throw new IllegalArgumentException(MessageFormat.format(
- KetchText.get().unsupportedVoterCount,
- Integer.valueOf(v.size()),
- validVoters));
- }
-
- LocalReplica me = findLocal(v);
- if (me == null) {
- throw new IllegalArgumentException(
- KetchText.get().localReplicaRequired);
- }
-
- lock.lock();
- try {
- voters = v.toArray(new KetchReplica[0]);
- followers = f.toArray(new KetchReplica[0]);
- self = me;
- } finally {
- lock.unlock();
- }
- }
-
- private static Collection<Integer> validVoterCounts() {
- @SuppressWarnings("boxing")
- Integer[] valid = {
- // An odd number of voting replicas is required.
- 1, 3, 5, 7, 9 };
- return Arrays.asList(valid);
- }
-
- private static LocalReplica findLocal(Collection<KetchReplica> voters) {
- for (KetchReplica r : voters) {
- if (r instanceof LocalReplica) {
- return (LocalReplica) r;
- }
- }
- return null;
- }
-
- /**
- * Get an instance of the repository for use by a leader thread.
- * <p>
- * The caller will close the repository.
- *
- * @return opened repository for use by the leader thread.
- * @throws java.io.IOException
- * cannot reopen the repository for the leader.
- */
- protected abstract Repository openRepository() throws IOException;
-
- /**
- * Queue a reference update proposal for consensus.
- * <p>
- * This method does not wait for consensus to be reached. The proposal is
- * checked to look for risks of conflicts, and then submitted into the queue
- * for distribution as soon as possible.
- * <p>
- * Callers must use {@link org.eclipse.jgit.internal.ketch.Proposal#await()}
- * to see if the proposal is done.
- *
- * @param proposal
- * the proposed reference updates to queue for consideration.
- * Once execution is complete the individual reference result
- * fields will be populated with the outcome.
- * @throws java.lang.InterruptedException
- * current thread was interrupted. The proposal may have been
- * aborted if it was not yet queued for execution.
- * @throws java.io.IOException
- * unrecoverable error preventing proposals from being attempted
- * by this leader.
- */
- public void queueProposal(Proposal proposal)
- throws InterruptedException, IOException {
- try {
- lock.lockInterruptibly();
- } catch (InterruptedException e) {
- proposal.abort();
- throw e;
- }
- try {
- if (refTree == null) {
- initialize();
- for (Proposal p : queued) {
- refTree.apply(p.getCommands());
- }
- } else if (roundHoldsReferenceToRefTree) {
- refTree = refTree.copy();
- roundHoldsReferenceToRefTree = false;
- }
-
- if (!refTree.apply(proposal.getCommands())) {
- // A conflict exists so abort the proposal.
- proposal.abort();
- return;
- }
-
- queued.add(proposal);
- proposal.notifyState(QUEUED);
-
- if (idle) {
- scheduleLeader();
- }
- } finally {
- lock.unlock();
- }
- }
-
- private void initialize() throws IOException {
- try (Repository git = openRepository(); RevWalk rw = new RevWalk(git)) {
- self.initialize(git);
-
- ObjectId accepted = self.getTxnAccepted();
- if (!ObjectId.zeroId().equals(accepted)) {
- RevCommit c = rw.parseCommit(accepted);
- headIndex = LogIndex.unknown(accepted);
- refTree = RefTree.read(rw.getObjectReader(), c.getTree());
- } else {
- headIndex = LogIndex.unknown(ObjectId.zeroId());
- refTree = RefTree.newEmptyTree();
- }
- }
- }
-
- private void scheduleLeader() {
- idle = false;
- system.getExecutor().execute(this::runLeader);
- }
-
- private void runLeader() {
- Round round;
- lock.lock();
- try {
- switch (state) {
- case CANDIDATE:
- round = new ElectionRound(this, headIndex);
- break;
-
- case LEADER:
- round = newProposalRound();
- break;
-
- case DEPOSED:
- case SHUTDOWN:
- default:
- log.warn("Leader cannot run {}", state); //$NON-NLS-1$
- // TODO(sop): Redirect proposals.
- return;
- }
- } finally {
- lock.unlock();
- }
-
- try {
- round.start();
- } catch (IOException e) {
- // TODO(sop) Depose leader if it cannot use its repository.
- log.error(KetchText.get().leaderFailedToStore, e);
- lock.lock();
- try {
- nextRound();
- } finally {
- lock.unlock();
- }
- }
- }
-
- private ProposalRound newProposalRound() {
- List<Proposal> todo = new ArrayList<>(queued);
- queued.clear();
- roundHoldsReferenceToRefTree = true;
- return new ProposalRound(this, headIndex, todo, refTree);
- }
-
- /** @return term of this leader's reign. */
- long getTerm() {
- return term;
- }
-
- /** @return end of the leader's log. */
- LogIndex getHead() {
- return headIndex;
- }
-
- /**
- * @return state leader knows it has committed across a quorum of replicas.
- */
- LogIndex getCommitted() {
- return committedIndex;
- }
-
- boolean isIdle() {
- return idle;
- }
-
- void runAsync(Round round) {
- lock.lock();
- try {
- // End of the log is this round. Once transport begins it is
- // reasonable to assume at least one replica will eventually get
- // this, and there is reasonable probability it commits.
- headIndex = round.acceptedNewIndex;
- runningRound = round;
-
- for (KetchReplica replica : voters) {
- replica.pushTxnAcceptedAsync(round);
- }
- for (KetchReplica replica : followers) {
- replica.pushTxnAcceptedAsync(round);
- }
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Asynchronous signal from a replica after completion.
- * <p>
- * Must be called while {@link #lock} is held by the replica.
- *
- * @param replica
- * replica posting a completion event.
- */
- void onReplicaUpdate(KetchReplica replica) {
- if (log.isDebugEnabled()) {
- log.debug("Replica {} finished:\n{}", //$NON-NLS-1$
- replica.describeForLog(), snapshot());
- }
-
- if (replica.getParticipation() == FOLLOWER_ONLY) {
- // Followers cannot vote, so votes haven't changed.
- return;
- } else if (runningRound == null) {
- // No round running, no need to tally votes.
- return;
- }
-
- assert headIndex.equals(runningRound.acceptedNewIndex);
- int matching = 0;
- for (KetchReplica r : voters) {
- if (r.hasAccepted(headIndex)) {
- matching++;
- }
- }
-
- int quorum = voters.length / 2 + 1;
- boolean success = matching >= quorum;
- if (!success) {
- return;
- }
-
- switch (state) {
- case CANDIDATE:
- term = ((ElectionRound) runningRound).getTerm();
- state = LEADER;
- if (log.isDebugEnabled()) {
- log.debug("Won election, running term " + term); //$NON-NLS-1$
- }
-
- //$FALL-THROUGH$
- case LEADER:
- committedIndex = headIndex;
- if (log.isDebugEnabled()) {
- log.debug("Committed {} in term {}", //$NON-NLS-1$
- committedIndex.describeForLog(),
- Long.valueOf(term));
- }
- nextRound();
- commitAsync(replica);
- notifySuccess(runningRound);
- if (log.isDebugEnabled()) {
- log.debug("Leader state:\n{}", snapshot()); //$NON-NLS-1$
- }
- break;
-
- default:
- log.debug("Leader ignoring replica while in {}", state); //$NON-NLS-1$
- break;
- }
- }
-
- private void notifySuccess(Round round) {
- // Drop the leader lock while notifying Proposal listeners.
- lock.unlock();
- try {
- round.success();
- } finally {
- lock.lock();
- }
- }
-
- private void commitAsync(KetchReplica caller) {
- for (KetchReplica r : voters) {
- if (r == caller) {
- continue;
- }
- if (r.shouldPushUnbatchedCommit(committedIndex, isIdle())) {
- r.pushCommitAsync(committedIndex);
- }
- }
- for (KetchReplica r : followers) {
- if (r == caller) {
- continue;
- }
- if (r.shouldPushUnbatchedCommit(committedIndex, isIdle())) {
- r.pushCommitAsync(committedIndex);
- }
- }
- }
-
- /** Schedule the next round; invoked while {@link #lock} is held. */
- void nextRound() {
- runningRound = null;
-
- if (queued.isEmpty()) {
- idle = true;
- } else {
- // Caller holds lock. Reschedule leader on a new thread so
- // the call stack can unwind and lock is not held unexpectedly
- // during prepare for the next round.
- scheduleLeader();
- }
- }
-
- /**
- * Snapshot this leader
- *
- * @return snapshot of this leader
- */
- public LeaderSnapshot snapshot() {
- lock.lock();
- try {
- LeaderSnapshot s = new LeaderSnapshot();
- s.state = state;
- s.term = term;
- s.headIndex = headIndex;
- s.committedIndex = committedIndex;
- s.idle = isIdle();
- for (KetchReplica r : voters) {
- s.replicas.add(r.snapshot());
- }
- for (KetchReplica r : followers) {
- s.replicas.add(r.snapshot());
- }
- return s;
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Gracefully shutdown this leader and cancel outstanding operations.
- */
- public void shutdown() {
- lock.lock();
- try {
- if (state != SHUTDOWN) {
- state = SHUTDOWN;
- for (KetchReplica r : voters) {
- r.shutdown();
- }
- for (KetchReplica r : followers) {
- r.shutdown();
- }
- }
- } finally {
- lock.unlock();
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public String toString() {
- return snapshot().toString();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeaderCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeaderCache.java
deleted file mode 100644
index e01fb3ae53..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeaderCache.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import java.net.URISyntaxException;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-import org.eclipse.jgit.internal.storage.dfs.DfsRepository;
-import org.eclipse.jgit.lib.Repository;
-
-/**
- * A cache of live leader instances, keyed by repository.
- * <p>
- * Ketch only assigns a leader to a repository when needed. If
- * {@link #get(Repository)} is called for a repository that does not have a
- * leader, the leader is created and added to the cache.
- */
-public class KetchLeaderCache {
- private final KetchSystem system;
- private final ConcurrentMap<String, KetchLeader> leaders;
- private final Lock startLock;
-
- /**
- * Initialize a new leader cache.
- *
- * @param system
- * system configuration for the leaders
- */
- public KetchLeaderCache(KetchSystem system) {
- this.system = system;
- leaders = new ConcurrentHashMap<>();
- startLock = new ReentrantLock(true /* fair */);
- }
-
- /**
- * Lookup the leader instance for a given repository.
- *
- * @param repo
- * repository to get the leader for.
- * @return the leader instance for the repository.
- * @throws java.net.URISyntaxException
- * remote configuration contains an invalid URL.
- */
- public KetchLeader get(Repository repo)
- throws URISyntaxException {
- String key = computeKey(repo);
- KetchLeader leader = leaders.get(key);
- if (leader != null) {
- return leader;
- }
- return startLeader(key, repo);
- }
-
- private KetchLeader startLeader(String key, Repository repo)
- throws URISyntaxException {
- startLock.lock();
- try {
- KetchLeader leader = leaders.get(key);
- if (leader != null) {
- return leader;
- }
- leader = system.createLeader(repo);
- leaders.put(key, leader);
- return leader;
- } finally {
- startLock.unlock();
- }
- }
-
- private static String computeKey(Repository repo) {
- if (repo instanceof DfsRepository) {
- DfsRepository dfs = (DfsRepository) repo;
- return dfs.getDescription().getRepositoryName();
- }
-
- if (repo.getDirectory() != null) {
- return repo.getDirectory().toURI().toString();
- }
-
- throw new IllegalArgumentException();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchPreReceive.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchPreReceive.java
deleted file mode 100644
index 1c9535f7be..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchPreReceive.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.eclipse.jgit.internal.ketch.Proposal.State.EXECUTED;
-import static org.eclipse.jgit.internal.ketch.Proposal.State.QUEUED;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
-
-import java.io.IOException;
-import java.util.Collection;
-
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.transport.PreReceiveHook;
-import org.eclipse.jgit.transport.ProgressSpinner;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.transport.ReceivePack;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * PreReceiveHook for handling push traffic in a Ketch system.
- * <p>
- * Install an instance on {@link org.eclipse.jgit.transport.ReceivePack} to
- * capture the commands and other connection state and relay them through the
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader}, allowing the leader to
- * gain consensus about the new reference state.
- */
-public class KetchPreReceive implements PreReceiveHook {
- private static final Logger log = LoggerFactory.getLogger(KetchPreReceive.class);
-
- private final KetchLeader leader;
-
- /**
- * Construct a hook executing updates through a
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader}.
- *
- * @param leader
- * leader for this repository.
- */
- public KetchPreReceive(KetchLeader leader) {
- this.leader = leader;
- }
-
- /** {@inheritDoc} */
- @Override
- public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> cmds) {
- cmds = ReceiveCommand.filter(cmds, NOT_ATTEMPTED);
- if (cmds.isEmpty()) {
- return;
- }
-
- try {
- Proposal proposal = new Proposal(rp.getRevWalk(), cmds)
- .setPushCertificate(rp.getPushCertificate())
- .setAuthor(rp.getRefLogIdent())
- .setMessage("push"); //$NON-NLS-1$
- leader.queueProposal(proposal);
- if (proposal.isDone()) {
- // This failed fast, e.g. conflict or bad precondition.
- return;
- }
-
- ProgressSpinner spinner = new ProgressSpinner(
- rp.getMessageOutputStream());
- if (proposal.getState() == QUEUED) {
- waitForQueue(proposal, spinner);
- }
- if (!proposal.isDone()) {
- waitForPropose(proposal, spinner);
- }
- } catch (IOException | InterruptedException e) {
- String msg = JGitText.get().transactionAborted;
- for (ReceiveCommand cmd : cmds) {
- if (cmd.getResult() == NOT_ATTEMPTED) {
- cmd.setResult(REJECTED_OTHER_REASON, msg);
- }
- }
- log.error(msg, e);
- }
- }
-
- private void waitForQueue(Proposal proposal, ProgressSpinner spinner)
- throws InterruptedException {
- spinner.beginTask(KetchText.get().waitingForQueue, 1, SECONDS);
- while (!proposal.awaitStateChange(QUEUED, 250, MILLISECONDS)) {
- spinner.update();
- }
- switch (proposal.getState()) {
- case RUNNING:
- default:
- spinner.endTask(KetchText.get().starting);
- break;
-
- case EXECUTED:
- spinner.endTask(KetchText.get().accepted);
- break;
-
- case ABORTED:
- spinner.endTask(KetchText.get().failed);
- break;
- }
- }
-
- private void waitForPropose(Proposal proposal, ProgressSpinner spinner)
- throws InterruptedException {
- spinner.beginTask(KetchText.get().proposingUpdates, 2, SECONDS);
- while (!proposal.await(250, MILLISECONDS)) {
- spinner.update();
- }
- spinner.endTask(proposal.getState() == EXECUTED
- ? KetchText.get().accepted
- : KetchText.get().failed);
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java
deleted file mode 100644
index a9a694a565..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java
+++ /dev/null
@@ -1,758 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static org.eclipse.jgit.internal.ketch.KetchReplica.CommitSpeed.BATCHED;
-import static org.eclipse.jgit.internal.ketch.KetchReplica.CommitSpeed.FAST;
-import static org.eclipse.jgit.internal.ketch.KetchReplica.State.CURRENT;
-import static org.eclipse.jgit.internal.ketch.KetchReplica.State.LAGGING;
-import static org.eclipse.jgit.internal.ketch.KetchReplica.State.OFFLINE;
-import static org.eclipse.jgit.internal.ketch.KetchReplica.State.UNKNOWN;
-import static org.eclipse.jgit.lib.Constants.HEAD;
-import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
-import static org.eclipse.jgit.transport.ReceiveCommand.Type.CREATE;
-
-import java.io.IOException;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Future;
-
-import org.eclipse.jgit.annotations.NonNull;
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.internal.storage.reftree.RefTree;
-import org.eclipse.jgit.lib.AnyObjectId;
-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;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.util.FileUtils;
-import org.eclipse.jgit.util.SystemReader;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A Ketch replica, either {@link org.eclipse.jgit.internal.ketch.LocalReplica}
- * or {@link org.eclipse.jgit.internal.ketch.RemoteGitReplica}.
- * <p>
- * Replicas can be either a stock Git replica, or a Ketch-aware replica.
- * <p>
- * A stock Git replica has no special knowledge of Ketch and simply stores
- * objects and references. Ketch communicates with the stock Git replica using
- * the Git push wire protocol. The
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader} commits an agreed upon
- * state by pushing all references to the Git replica, for example
- * {@code "refs/heads/master"} is pushed during commit. Stock Git replicas use
- * {@link org.eclipse.jgit.internal.ketch.KetchReplica.CommitMethod#ALL_REFS} to
- * record the final state.
- * <p>
- * Ketch-aware replicas understand the {@code RefTree} sent during the proposal
- * and during commit are able to update their own reference space to match the
- * state represented by the {@code RefTree}. Ketch-aware replicas typically use
- * a {@link org.eclipse.jgit.internal.storage.reftree.RefTreeDatabase} and
- * {@link org.eclipse.jgit.internal.ketch.KetchReplica.CommitMethod#TXN_COMMITTED}
- * to record the final state.
- * <p>
- * KetchReplica instances are tightly coupled with a single
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader}. Some state may be
- * accessed by the leader thread and uses the leader's own
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader#lock} to protect shared
- * data.
- */
-public abstract class KetchReplica {
- static final Logger log = LoggerFactory.getLogger(KetchReplica.class);
- private static final byte[] PEEL = { ' ', '^' };
-
- /** Participation of a replica in establishing consensus. */
- public enum Participation {
- /** Replica can vote. */
- FULL,
-
- /** Replica does not vote, but tracks leader. */
- FOLLOWER_ONLY;
- }
-
- /** How this replica wants to receive Ketch commit operations. */
- public enum CommitMethod {
- /** All references are pushed to the peer as standard Git. */
- ALL_REFS,
-
- /** Only {@code refs/txn/committed} is written/updated. */
- TXN_COMMITTED;
- }
-
- /** Delay before committing to a replica. */
- public enum CommitSpeed {
- /**
- * Send the commit immediately, even if it could be batched with the
- * next proposal.
- */
- FAST,
-
- /**
- * If the next proposal is available, batch the commit with it,
- * otherwise just send the commit. This generates less network use, but
- * may provide slower consistency on the replica.
- */
- BATCHED;
- }
-
- /** Current state of a replica. */
- public enum State {
- /** Leader has not yet contacted the replica. */
- UNKNOWN,
-
- /** Replica is behind the consensus. */
- LAGGING,
-
- /** Replica matches the consensus. */
- CURRENT,
-
- /** Replica has a different (or unknown) history. */
- DIVERGENT,
-
- /** Replica's history contains the leader's history. */
- AHEAD,
-
- /** Replica can not be contacted. */
- OFFLINE;
- }
-
- private final KetchLeader leader;
- private final String replicaName;
- private final Participation participation;
- private final CommitMethod commitMethod;
- private final CommitSpeed commitSpeed;
- private final long minRetryMillis;
- private final long maxRetryMillis;
- private final Map<ObjectId, List<ReceiveCommand>> staged;
- private final Map<String, ReceiveCommand> running;
- private final Map<String, ReceiveCommand> waiting;
- private final List<ReplicaPushRequest> queued;
-
- /**
- * Value known for {@code "refs/txn/accepted"}.
- * <p>
- * Raft literature refers to this as {@code matchIndex}.
- */
- private ObjectId txnAccepted;
-
- /**
- * Value known for {@code "refs/txn/committed"}.
- * <p>
- * Raft literature refers to this as {@code commitIndex}. In traditional
- * Raft this is a state variable inside the follower implementation, but
- * Ketch keeps it in the leader.
- */
- private ObjectId txnCommitted;
-
- /** What is happening with this replica. */
- private State state = UNKNOWN;
- private String error;
-
- /** Scheduled retry due to communication failure. */
- private Future<?> retryFuture;
- private long lastRetryMillis;
- private long retryAtMillis;
-
- /**
- * Configure a replica representation.
- *
- * @param leader
- * instance this replica follows.
- * @param name
- * unique-ish name identifying this replica for debugging.
- * @param cfg
- * how Ketch should treat the replica.
- */
- protected KetchReplica(KetchLeader leader, String name, ReplicaConfig cfg) {
- this.leader = leader;
- this.replicaName = name;
- this.participation = cfg.getParticipation();
- this.commitMethod = cfg.getCommitMethod();
- this.commitSpeed = cfg.getCommitSpeed();
- this.minRetryMillis = cfg.getMinRetry(MILLISECONDS);
- this.maxRetryMillis = cfg.getMaxRetry(MILLISECONDS);
- this.staged = new HashMap<>();
- this.running = new HashMap<>();
- this.waiting = new HashMap<>();
- this.queued = new ArrayList<>(4);
- }
-
- /**
- * Get system configuration.
- *
- * @return system configuration.
- */
- public KetchSystem getSystem() {
- return getLeader().getSystem();
- }
-
- /**
- * Get leader instance this replica follows.
- *
- * @return leader instance this replica follows.
- */
- public KetchLeader getLeader() {
- return leader;
- }
-
- /**
- * Get unique-ish name for debugging.
- *
- * @return unique-ish name for debugging.
- */
- public String getName() {
- return replicaName;
- }
-
- /**
- * Get description of this replica for error/debug logging purposes.
- *
- * @return description of this replica for error/debug logging purposes.
- */
- protected String describeForLog() {
- return getName();
- }
-
- /**
- * Get how the replica participates in this Ketch system.
- *
- * @return how the replica participates in this Ketch system.
- */
- public Participation getParticipation() {
- return participation;
- }
-
- /**
- * Get how Ketch will commit to the repository.
- *
- * @return how Ketch will commit to the repository.
- */
- public CommitMethod getCommitMethod() {
- return commitMethod;
- }
-
- /**
- * Get when Ketch will commit to the repository.
- *
- * @return when Ketch will commit to the repository.
- */
- public CommitSpeed getCommitSpeed() {
- return commitSpeed;
- }
-
- /**
- * Called by leader to perform graceful shutdown.
- * <p>
- * Default implementation cancels any scheduled retry. Subclasses may add
- * additional logic before or after calling {@code super.shutdown()}.
- * <p>
- * Called with {@link org.eclipse.jgit.internal.ketch.KetchLeader#lock} held
- * by caller.
- */
- protected void shutdown() {
- Future<?> f = retryFuture;
- if (f != null) {
- retryFuture = null;
- f.cancel(true);
- }
- }
-
- ReplicaSnapshot snapshot() {
- ReplicaSnapshot s = new ReplicaSnapshot(this);
- s.accepted = txnAccepted;
- s.committed = txnCommitted;
- s.state = state;
- s.error = error;
- s.retryAtMillis = waitingForRetry() ? retryAtMillis : 0;
- return s;
- }
-
- /**
- * Update the leader's view of the replica after a poll.
- * <p>
- * Called with {@link KetchLeader#lock} held by caller.
- *
- * @param refs
- * map of refs from the replica.
- */
- void initialize(Map<String, Ref> refs) {
- if (txnAccepted == null) {
- txnAccepted = getId(refs.get(getSystem().getTxnAccepted()));
- }
- if (txnCommitted == null) {
- txnCommitted = getId(refs.get(getSystem().getTxnCommitted()));
- }
- }
-
- ObjectId getTxnAccepted() {
- return txnAccepted;
- }
-
- boolean hasAccepted(LogIndex id) {
- return equals(txnAccepted, id);
- }
-
- private static boolean equals(@Nullable ObjectId a, LogIndex b) {
- return a != null && b != null && AnyObjectId.isEqual(a, b);
- }
-
- /**
- * Schedule a proposal round with the replica.
- * <p>
- * Called with {@link KetchLeader#lock} held by caller.
- *
- * @param round
- * current round being run by the leader.
- */
- void pushTxnAcceptedAsync(Round round) {
- List<ReceiveCommand> cmds = new ArrayList<>();
- if (commitSpeed == BATCHED) {
- LogIndex committedIndex = leader.getCommitted();
- if (equals(txnAccepted, committedIndex)
- && !equals(txnCommitted, committedIndex)) {
- prepareTxnCommitted(cmds, committedIndex);
- }
- }
-
- // TODO(sop) Lagging replicas should build accept on the fly.
- if (round.stageCommands != null) {
- for (ReceiveCommand cmd : round.stageCommands) {
- // TODO(sop): Do not send certain object graphs to replica.
- cmds.add(copy(cmd));
- }
- }
- cmds.add(new ReceiveCommand(
- round.acceptedOldIndex, round.acceptedNewIndex,
- getSystem().getTxnAccepted()));
- pushAsync(new ReplicaPushRequest(this, cmds));
- }
-
- private static ReceiveCommand copy(ReceiveCommand c) {
- return new ReceiveCommand(c.getOldId(), c.getNewId(), c.getRefName());
- }
-
- boolean shouldPushUnbatchedCommit(LogIndex committed, boolean leaderIdle) {
- return (leaderIdle || commitSpeed == FAST) && hasAccepted(committed);
- }
-
- void pushCommitAsync(LogIndex committed) {
- List<ReceiveCommand> cmds = new ArrayList<>();
- prepareTxnCommitted(cmds, committed);
- pushAsync(new ReplicaPushRequest(this, cmds));
- }
-
- private void prepareTxnCommitted(List<ReceiveCommand> cmds,
- ObjectId committed) {
- removeStaged(cmds, committed);
- cmds.add(new ReceiveCommand(
- txnCommitted, committed,
- getSystem().getTxnCommitted()));
- }
-
- private void removeStaged(List<ReceiveCommand> cmds, ObjectId committed) {
- List<ReceiveCommand> a = staged.remove(committed);
- if (a != null) {
- delete(cmds, a);
- }
- if (staged.isEmpty() || !(committed instanceof LogIndex)) {
- return;
- }
-
- LogIndex committedIndex = (LogIndex) committed;
- Iterator<Map.Entry<ObjectId, List<ReceiveCommand>>> itr = staged
- .entrySet().iterator();
- while (itr.hasNext()) {
- Map.Entry<ObjectId, List<ReceiveCommand>> e = itr.next();
- if (e.getKey() instanceof LogIndex) {
- LogIndex stagedIndex = (LogIndex) e.getKey();
- if (stagedIndex.isBefore(committedIndex)) {
- delete(cmds, e.getValue());
- itr.remove();
- }
- }
- }
- }
-
- private static void delete(List<ReceiveCommand> cmds,
- List<ReceiveCommand> createCmds) {
- for (ReceiveCommand cmd : createCmds) {
- ObjectId id = cmd.getNewId();
- String name = cmd.getRefName();
- cmds.add(new ReceiveCommand(id, ObjectId.zeroId(), name));
- }
- }
-
- /**
- * Determine the next push for this replica (if any) and start it.
- * <p>
- * If the replica has successfully accepted the committed state of the
- * leader, this method will push all references to the replica using the
- * configured {@link CommitMethod}.
- * <p>
- * If the replica is {@link State#LAGGING} this method will begin catch up
- * by sending a more recent {@code refs/txn/accepted}.
- * <p>
- * Must be invoked with {@link KetchLeader#lock} held by caller.
- */
- private void runNextPushRequest() {
- LogIndex committed = leader.getCommitted();
- if (!equals(txnCommitted, committed)
- && shouldPushUnbatchedCommit(committed, leader.isIdle())) {
- pushCommitAsync(committed);
- }
-
- if (queued.isEmpty() || !running.isEmpty() || waitingForRetry()) {
- return;
- }
-
- // Collapse all queued requests into a single request.
- Map<String, ReceiveCommand> cmdMap = new HashMap<>();
- for (ReplicaPushRequest req : queued) {
- for (ReceiveCommand cmd : req.getCommands()) {
- String name = cmd.getRefName();
- ReceiveCommand old = cmdMap.remove(name);
- if (old != null) {
- cmd = new ReceiveCommand(
- old.getOldId(), cmd.getNewId(),
- name);
- }
- cmdMap.put(name, cmd);
- }
- }
- queued.clear();
- waiting.clear();
-
- List<ReceiveCommand> next = new ArrayList<>(cmdMap.values());
- for (ReceiveCommand cmd : next) {
- running.put(cmd.getRefName(), cmd);
- }
- startPush(new ReplicaPushRequest(this, next));
- }
-
- private void pushAsync(ReplicaPushRequest req) {
- if (defer(req)) {
- // TODO(sop) Collapse during long retry outage.
- for (ReceiveCommand cmd : req.getCommands()) {
- waiting.put(cmd.getRefName(), cmd);
- }
- queued.add(req);
- } else {
- for (ReceiveCommand cmd : req.getCommands()) {
- running.put(cmd.getRefName(), cmd);
- }
- startPush(req);
- }
- }
-
- private boolean defer(ReplicaPushRequest req) {
- if (waitingForRetry()) {
- // Prior communication failure; everything is deferred.
- return true;
- }
-
- for (ReceiveCommand nextCmd : req.getCommands()) {
- ReceiveCommand priorCmd = waiting.get(nextCmd.getRefName());
- if (priorCmd == null) {
- priorCmd = running.get(nextCmd.getRefName());
- }
- if (priorCmd != null) {
- // Another request pending on same ref; that must go first.
- // Verify priorCmd.newId == nextCmd.oldId?
- return true;
- }
- }
- return false;
- }
-
- private boolean waitingForRetry() {
- Future<?> f = retryFuture;
- return f != null && !f.isDone();
- }
-
- private void retryLater(ReplicaPushRequest req) {
- Collection<ReceiveCommand> cmds = req.getCommands();
- for (ReceiveCommand cmd : cmds) {
- cmd.setResult(NOT_ATTEMPTED, null);
- if (!waiting.containsKey(cmd.getRefName())) {
- waiting.put(cmd.getRefName(), cmd);
- }
- }
- queued.add(0, new ReplicaPushRequest(this, cmds));
-
- if (!waitingForRetry()) {
- long delay = FileUtils
- .delay(lastRetryMillis, minRetryMillis, maxRetryMillis);
- if (log.isDebugEnabled()) {
- log.debug("Retrying {} after {} ms", //$NON-NLS-1$
- describeForLog(), Long.valueOf(delay));
- }
- lastRetryMillis = delay;
- retryAtMillis = SystemReader.getInstance().getCurrentTime() + delay;
- retryFuture = getSystem().getExecutor()
- .schedule(new WeakRetryPush(this), delay, MILLISECONDS);
- }
- }
-
- /** Weakly holds a retrying replica, allowing it to garbage collect. */
- static class WeakRetryPush extends WeakReference<KetchReplica>
- implements Callable<Void> {
- WeakRetryPush(KetchReplica r) {
- super(r);
- }
-
- @Override
- public Void call() throws Exception {
- KetchReplica r = get();
- if (r != null) {
- r.doRetryPush();
- }
- return null;
- }
- }
-
- private void doRetryPush() {
- leader.lock.lock();
- try {
- retryFuture = null;
- runNextPushRequest();
- } finally {
- leader.lock.unlock();
- }
- }
-
- /**
- * Begin executing a single push.
- * <p>
- * This method must move processing onto another thread. Called with
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader#lock} held by caller.
- *
- * @param req
- * the request to send to the replica.
- */
- protected abstract void startPush(ReplicaPushRequest req);
-
- /**
- * Callback from {@link ReplicaPushRequest} upon success or failure.
- * <p>
- * Acquires the {@link KetchLeader#lock} and updates the leader's internal
- * knowledge about this replica to reflect what has been learned during a
- * push to the replica. In some cases of divergence this method may take
- * some time to determine how the replica has diverged; to reduce contention
- * this is evaluated before acquiring the leader lock.
- *
- * @param repo
- * local repository instance used by the push thread.
- * @param req
- * push request just attempted.
- */
- void afterPush(@Nullable Repository repo, ReplicaPushRequest req) {
- ReceiveCommand acceptCmd = null;
- ReceiveCommand commitCmd = null;
- List<ReceiveCommand> stages = null;
-
- for (ReceiveCommand cmd : req.getCommands()) {
- String name = cmd.getRefName();
- if (name.equals(getSystem().getTxnAccepted())) {
- acceptCmd = cmd;
- } else if (name.equals(getSystem().getTxnCommitted())) {
- commitCmd = cmd;
- } else if (cmd.getResult() == OK && cmd.getType() == CREATE
- && name.startsWith(getSystem().getTxnStage())) {
- if (stages == null) {
- stages = new ArrayList<>();
- }
- stages.add(cmd);
- }
- }
-
- State newState = null;
- ObjectId acceptId = readId(req, acceptCmd);
- if (repo != null && acceptCmd != null && acceptCmd.getResult() != OK
- && req.getException() == null) {
- try (LagCheck lag = new LagCheck(this, repo)) {
- newState = lag.check(acceptId, acceptCmd);
- acceptId = lag.getRemoteId();
- }
- }
-
- leader.lock.lock();
- try {
- for (ReceiveCommand cmd : req.getCommands()) {
- running.remove(cmd.getRefName());
- }
-
- Throwable err = req.getException();
- if (err != null) {
- state = OFFLINE;
- error = err.toString();
- retryLater(req);
- leader.onReplicaUpdate(this);
- return;
- }
-
- lastRetryMillis = 0;
- error = null;
- updateView(req, acceptId, commitCmd);
-
- if (acceptCmd != null && acceptCmd.getResult() == OK) {
- state = hasAccepted(leader.getHead()) ? CURRENT : LAGGING;
- if (stages != null) {
- staged.put(acceptCmd.getNewId(), stages);
- }
- } else if (newState != null) {
- state = newState;
- }
-
- leader.onReplicaUpdate(this);
- runNextPushRequest();
- } finally {
- leader.lock.unlock();
- }
- }
-
- private void updateView(ReplicaPushRequest req, @Nullable ObjectId acceptId,
- ReceiveCommand commitCmd) {
- if (acceptId != null) {
- txnAccepted = acceptId;
- }
-
- ObjectId committed = readId(req, commitCmd);
- if (committed != null) {
- txnCommitted = committed;
- } else if (acceptId != null && txnCommitted == null) {
- // Initialize during first conversation.
- Map<String, Ref> adv = req.getRefs();
- if (adv != null) {
- Ref refs = adv.get(getSystem().getTxnCommitted());
- txnCommitted = getId(refs);
- }
- }
- }
-
- @Nullable
- private static ObjectId readId(ReplicaPushRequest req,
- @Nullable ReceiveCommand cmd) {
- if (cmd == null) {
- // Ref was not in the command list, do not trust advertisement.
- return null;
-
- } else if (cmd.getResult() == OK) {
- // Currently at newId.
- return cmd.getNewId();
- }
-
- Map<String, Ref> refs = req.getRefs();
- return refs != null ? getId(refs.get(cmd.getRefName())) : null;
- }
-
- /**
- * Fetch objects from the remote using the calling thread.
- * <p>
- * Called without {@link org.eclipse.jgit.internal.ketch.KetchLeader#lock}.
- *
- * @param repo
- * local repository to fetch objects into.
- * @param req
- * the request to fetch from a replica.
- * @throws java.io.IOException
- * communication with the replica was not possible.
- */
- protected abstract void blockingFetch(Repository repo,
- ReplicaFetchRequest req) throws IOException;
-
- /**
- * Build a list of commands to commit
- * {@link org.eclipse.jgit.internal.ketch.KetchReplica.CommitMethod#ALL_REFS}.
- *
- * @param git
- * local leader repository to read committed state from.
- * @param current
- * all known references in the replica's repository. Typically
- * this comes from a push advertisement.
- * @param committed
- * state being pushed to {@code refs/txn/committed}.
- * @return commands to update during commit.
- * @throws java.io.IOException
- * cannot read the committed state.
- */
- protected Collection<ReceiveCommand> prepareCommit(Repository git,
- Map<String, Ref> current, ObjectId committed) throws IOException {
- List<ReceiveCommand> delta = new ArrayList<>();
- Map<String, Ref> remote = new HashMap<>(current);
- try (RevWalk rw = new RevWalk(git);
- TreeWalk tw = new TreeWalk(rw.getObjectReader())) {
- tw.setRecursive(true);
- tw.addTree(rw.parseCommit(committed).getTree());
- while (tw.next()) {
- if (tw.getRawMode(0) != TYPE_GITLINK
- || tw.isPathSuffix(PEEL, 2)) {
- // Symbolic references cannot be pushed.
- // Caching peeled values is handled remotely.
- continue;
- }
-
- // TODO(sop) Do not send certain ref names to replica.
- String name = RefTree.refName(tw.getPathString());
- Ref oldRef = remote.remove(name);
- ObjectId oldId = getId(oldRef);
- ObjectId newId = tw.getObjectId(0);
- if (!AnyObjectId.isEqual(oldId, newId)) {
- delta.add(new ReceiveCommand(oldId, newId, name));
- }
- }
- }
-
- // Delete any extra references not in the committed state.
- for (Ref ref : remote.values()) {
- if (canDelete(ref)) {
- delta.add(new ReceiveCommand(
- ref.getObjectId(), ObjectId.zeroId(),
- ref.getName()));
- }
- }
- return delta;
- }
-
- boolean canDelete(Ref ref) {
- String name = ref.getName();
- if (HEAD.equals(name)) {
- return false;
- }
- if (name.startsWith(getSystem().getTxnNamespace())) {
- return false;
- }
- // TODO(sop) Do not delete precious names from replica.
- return true;
- }
-
- @NonNull
- static ObjectId getId(@Nullable Ref ref) {
- if (ref != null) {
- ObjectId id = ref.getObjectId();
- if (id != null) {
- return id;
- }
- }
- return ObjectId.zeroId();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchSystem.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchSystem.java
deleted file mode 100644
index 8ad1d6033a..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchSystem.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static org.eclipse.jgit.internal.ketch.KetchConstants.ACCEPTED;
-import static org.eclipse.jgit.internal.ketch.KetchConstants.COMMITTED;
-import static org.eclipse.jgit.internal.ketch.KetchConstants.CONFIG_KEY_TYPE;
-import static org.eclipse.jgit.internal.ketch.KetchConstants.CONFIG_SECTION_KETCH;
-import static org.eclipse.jgit.internal.ketch.KetchConstants.DEFAULT_TXN_NAMESPACE;
-import static org.eclipse.jgit.internal.ketch.KetchConstants.STAGE;
-import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_NAME;
-import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_REMOTE;
-
-import java.net.URISyntaxException;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.RemoteConfig;
-import org.eclipse.jgit.transport.URIish;
-import org.eclipse.jgit.util.time.MonotonicClock;
-import org.eclipse.jgit.util.time.MonotonicSystemClock;
-import org.eclipse.jgit.util.time.ProposedTimestamp;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Ketch system-wide configuration.
- * <p>
- * This class provides useful defaults for testing and small proof of concepts.
- * Full scale installations are expected to subclass and override methods to
- * provide consistent configuration across all managed repositories.
- * <p>
- * Servers should configure their own
- * {@link java.util.concurrent.ScheduledExecutorService}.
- */
-public class KetchSystem {
- private static final Random RNG = new Random();
-
- /**
- * Get default executor, one thread per available processor.
- *
- * @return default executor, one thread per available processor.
- */
- public static ScheduledExecutorService defaultExecutor() {
- return DefaultExecutorHolder.I;
- }
-
- private final ScheduledExecutorService executor;
- private final MonotonicClock clock;
- private final String txnNamespace;
- private final String txnAccepted;
- private final String txnCommitted;
- private final String txnStage;
-
- /**
- * Create a default system with a thread pool of 1 thread per CPU.
- */
- public KetchSystem() {
- this(defaultExecutor(), new MonotonicSystemClock(), DEFAULT_TXN_NAMESPACE);
- }
-
- /**
- * Create a Ketch system with the provided executor service.
- *
- * @param executor
- * thread pool to run background operations.
- * @param clock
- * clock to create timestamps.
- * @param txnNamespace
- * reference namespace for the RefTree graph and associated
- * transaction state. Must begin with {@code "refs/"} and end
- * with {@code '/'}, for example {@code "refs/txn/"}.
- */
- public KetchSystem(ScheduledExecutorService executor, MonotonicClock clock,
- String txnNamespace) {
- this.executor = executor;
- this.clock = clock;
- this.txnNamespace = txnNamespace;
- this.txnAccepted = txnNamespace + ACCEPTED;
- this.txnCommitted = txnNamespace + COMMITTED;
- this.txnStage = txnNamespace + STAGE;
- }
-
- /**
- * Get executor to perform background operations.
- *
- * @return executor to perform background operations.
- */
- public ScheduledExecutorService getExecutor() {
- return executor;
- }
-
- /**
- * Get clock to obtain timestamps from.
- *
- * @return clock to obtain timestamps from.
- */
- public MonotonicClock getClock() {
- return clock;
- }
-
- /**
- * Get how long the leader will wait for the {@link #getClock()}'s
- * {@code ProposedTimestamp} used in commits proposed to the RefTree graph
- * ({@link #getTxnAccepted()})
- *
- * @return how long the leader will wait for the {@link #getClock()}'s
- * {@code ProposedTimestamp} used in commits proposed to the RefTree
- * graph ({@link #getTxnAccepted()}). Defaults to 5 seconds.
- */
- public Duration getMaxWaitForMonotonicClock() {
- return Duration.ofSeconds(5);
- }
-
- /**
- * Whether elections should require monotonically increasing commit
- * timestamps
- *
- * @return {@code true} if elections should require monotonically increasing
- * commit timestamps. This requires a very good
- * {@link org.eclipse.jgit.util.time.MonotonicClock}.
- */
- public boolean requireMonotonicLeaderElections() {
- return false;
- }
-
- /**
- * Get the namespace used for the RefTree graph and transaction management.
- *
- * @return reference namespace such as {@code "refs/txn/"}.
- */
- public String getTxnNamespace() {
- return txnNamespace;
- }
-
- /**
- * Get name of the accepted RefTree graph.
- *
- * @return name of the accepted RefTree graph.
- */
- public String getTxnAccepted() {
- return txnAccepted;
- }
-
- /**
- * Get name of the committed RefTree graph.
- *
- * @return name of the committed RefTree graph.
- */
- public String getTxnCommitted() {
- return txnCommitted;
- }
-
- /**
- * Get prefix for staged objects, e.g. {@code "refs/txn/stage/"}.
- *
- * @return prefix for staged objects, e.g. {@code "refs/txn/stage/"}.
- */
- public String getTxnStage() {
- return txnStage;
- }
-
- /**
- * Create new committer {@code PersonIdent} for ketch system
- *
- * @param time
- * timestamp for the committer.
- * @return identity line for the committer header of a RefTreeGraph.
- */
- public PersonIdent newCommitter(ProposedTimestamp time) {
- String name = "ketch"; //$NON-NLS-1$
- String email = "ketch@system"; //$NON-NLS-1$
- return new PersonIdent(name, email, time);
- }
-
- /**
- * Construct a random tag to identify a candidate during leader election.
- * <p>
- * Multiple processes trying to elect themselves leaders at exactly the same
- * time (rounded to seconds) using the same
- * {@link #newCommitter(ProposedTimestamp)} identity strings, for the same
- * term, may generate the same ObjectId for the election commit and falsely
- * assume they have both won.
- * <p>
- * Candidates add this tag to their election ballot commit to disambiguate
- * the election. The tag only needs to be unique for a given triplet of
- * {@link #newCommitter(ProposedTimestamp)}, system time (rounded to
- * seconds), and term. If every replica in the system uses a unique
- * {@code newCommitter} (such as including the host name after the
- * {@code "@"} in the email address) the tag could be the empty string.
- * <p>
- * The default implementation generates a few bytes of random data.
- *
- * @return unique tag; null or empty string if {@code newCommitter()} is
- * sufficiently unique to identify the leader.
- */
- @Nullable
- public String newLeaderTag() {
- int n = RNG.nextInt(1 << (6 * 4));
- return String.format("%06x", Integer.valueOf(n)); //$NON-NLS-1$
- }
-
- /**
- * Construct the KetchLeader instance of a repository.
- *
- * @param repo
- * local repository stored by the leader.
- * @return leader instance.
- * @throws java.net.URISyntaxException
- * a follower configuration contains an unsupported URI.
- */
- public KetchLeader createLeader(Repository repo)
- throws URISyntaxException {
- KetchLeader leader = new KetchLeader(this) {
- @Override
- protected Repository openRepository() {
- repo.incrementOpen();
- return repo;
- }
- };
- leader.setReplicas(createReplicas(leader, repo));
- return leader;
- }
-
- /**
- * Get the collection of replicas for a repository.
- * <p>
- * The collection of replicas must include the local repository.
- *
- * @param leader
- * the leader driving these replicas.
- * @param repo
- * repository to get the replicas of.
- * @return collection of replicas for the specified repository.
- * @throws java.net.URISyntaxException
- * a configured URI is invalid.
- */
- protected List<KetchReplica> createReplicas(KetchLeader leader,
- Repository repo) throws URISyntaxException {
- List<KetchReplica> replicas = new ArrayList<>();
- Config cfg = repo.getConfig();
- String localName = getLocalName(cfg);
- for (String name : cfg.getSubsections(CONFIG_KEY_REMOTE)) {
- if (!hasParticipation(cfg, name)) {
- continue;
- }
-
- ReplicaConfig kc = ReplicaConfig.newFromConfig(cfg, name);
- if (name.equals(localName)) {
- replicas.add(new LocalReplica(leader, name, kc));
- continue;
- }
-
- RemoteConfig rc = new RemoteConfig(cfg, name);
- List<URIish> uris = rc.getPushURIs();
- if (uris.isEmpty()) {
- uris = rc.getURIs();
- }
- for (URIish uri : uris) {
- String n = uris.size() == 1 ? name : uri.getHost();
- replicas.add(new RemoteGitReplica(leader, n, uri, kc, rc));
- }
- }
- return replicas;
- }
-
- private static boolean hasParticipation(Config cfg, String name) {
- return cfg.getString(CONFIG_KEY_REMOTE, name, CONFIG_KEY_TYPE) != null;
- }
-
- private static String getLocalName(Config cfg) {
- return cfg.getString(CONFIG_SECTION_KETCH, null, CONFIG_KEY_NAME);
- }
-
- static class DefaultExecutorHolder {
- private static final Logger log = LoggerFactory.getLogger(KetchSystem.class);
- static final ScheduledExecutorService I = create();
-
- private static ScheduledExecutorService create() {
- int cores = Runtime.getRuntime().availableProcessors();
- int threads = Math.max(5, cores);
- log.info("Using {} threads", Integer.valueOf(threads)); //$NON-NLS-1$
- return Executors.newScheduledThreadPool(
- threads,
- new ThreadFactory() {
- private final AtomicInteger threadCnt = new AtomicInteger();
-
- @Override
- public Thread newThread(Runnable r) {
- int id = threadCnt.incrementAndGet();
- Thread thr = new Thread(r);
- thr.setName("KetchExecutor-" + id); //$NON-NLS-1$
- return thr;
- }
- });
- }
-
- private DefaultExecutorHolder() {
- }
- }
-
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchText.java
deleted file mode 100644
index 6f9038bbdf..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchText.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import org.eclipse.jgit.nls.NLS;
-import org.eclipse.jgit.nls.TranslationBundle;
-
-/**
- * Translation bundle for the Ketch implementation.
- */
-public class KetchText extends TranslationBundle {
- /**
- * Get an instance of this translation bundle.
- *
- * @return instance of this translation bundle.
- */
- public static KetchText get() {
- return NLS.getBundleFor(KetchText.class);
- }
-
- // @formatter:off
- /***/ public String accepted;
- /***/ public String cannotFetchFromLocalReplica;
- /***/ public String failed;
- /***/ public String invalidFollowerUri;
- /***/ public String leaderFailedToStore;
- /***/ public String localReplicaRequired;
- /***/ public String mismatchedTxnNamespace;
- /***/ public String outsideTxnNamespace;
- /***/ public String proposingUpdates;
- /***/ public String queuedProposalFailedToApply;
- /***/ public String starting;
- /***/ public String unsupportedVoterCount;
- /***/ public String waitingForQueue;
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java
deleted file mode 100644
index 1f8384ff76..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static org.eclipse.jgit.internal.ketch.KetchReplica.State.AHEAD;
-import static org.eclipse.jgit.internal.ketch.KetchReplica.State.DIVERGENT;
-import static org.eclipse.jgit.internal.ketch.KetchReplica.State.LAGGING;
-import static org.eclipse.jgit.internal.ketch.KetchReplica.State.UNKNOWN;
-import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Map;
-
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-/**
- * A helper to check if a {@link KetchReplica} is ahead or behind the leader.
- */
-class LagCheck implements AutoCloseable {
- private final KetchReplica replica;
- private final Repository repo;
- private RevWalk rw;
- private ObjectId remoteId;
-
- LagCheck(KetchReplica replica, Repository repo) {
- this.replica = replica;
- this.repo = repo;
- initRevWalk();
- }
-
- private void initRevWalk() {
- if (rw != null) {
- rw.close();
- }
-
- rw = new RevWalk(repo);
- rw.setRetainBody(false);
- }
-
- /** {@inheritDoc} */
- @Override
- public void close() {
- if (rw != null) {
- rw.close();
- rw = null;
- }
- }
-
- ObjectId getRemoteId() {
- return remoteId;
- }
-
- KetchReplica.State check(ObjectId acceptId, ReceiveCommand acceptCmd) {
- remoteId = acceptId;
- if (remoteId == null) {
- // Nothing advertised by the replica, value is unknown.
- return UNKNOWN;
- }
-
- if (AnyObjectId.isEqual(remoteId, ObjectId.zeroId())) {
- // Replica does not have the txnAccepted reference.
- return LAGGING;
- }
-
- try {
- RevCommit remote;
- try {
- remote = parseRemoteCommit(acceptCmd.getRefName());
- } catch (RefGoneException gone) {
- // Replica does not have the txnAccepted reference.
- return LAGGING;
- } catch (MissingObjectException notFound) {
- // Local repository does not know this commit so it cannot
- // be including the replica's log.
- return DIVERGENT;
- }
-
- RevCommit head = rw.parseCommit(acceptCmd.getNewId());
- if (rw.isMergedInto(remote, head)) {
- return LAGGING;
- }
-
- // TODO(sop) Check term to see if my leader was deposed.
- if (rw.isMergedInto(head, remote)) {
- return AHEAD;
- }
- return DIVERGENT;
- } catch (IOException err) {
- KetchReplica.log.error(String.format(
- "Cannot compare %s", //$NON-NLS-1$
- acceptCmd.getRefName()), err);
- return UNKNOWN;
- }
- }
-
- private RevCommit parseRemoteCommit(String refName)
- throws IOException, MissingObjectException, RefGoneException {
- try {
- return rw.parseCommit(remoteId);
- } catch (MissingObjectException notLocal) {
- // Fall through and try to acquire the object by fetching it.
- }
-
- ReplicaFetchRequest fetch = new ReplicaFetchRequest(
- Collections.singleton(refName),
- Collections.<ObjectId> emptySet());
- try {
- replica.blockingFetch(repo, fetch);
- } catch (IOException fetchErr) {
- KetchReplica.log.error(String.format(
- "Cannot fetch %s (%s) from %s", //$NON-NLS-1$
- remoteId.abbreviate(8).name(), refName,
- replica.describeForLog()), fetchErr);
- throw new MissingObjectException(remoteId, OBJ_COMMIT);
- }
-
- Map<String, Ref> adv = fetch.getRefs();
- if (adv == null) {
- throw new MissingObjectException(remoteId, OBJ_COMMIT);
- }
-
- Ref ref = adv.get(refName);
- if (ref == null || ref.getObjectId() == null) {
- throw new RefGoneException();
- }
-
- initRevWalk();
- remoteId = ref.getObjectId();
- return rw.parseCommit(remoteId);
- }
-
- private static class RefGoneException extends Exception {
- private static final long serialVersionUID = 1L;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LeaderSnapshot.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LeaderSnapshot.java
deleted file mode 100644
index ce0672c168..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LeaderSnapshot.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static org.eclipse.jgit.internal.ketch.KetchReplica.State.OFFLINE;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.lib.ObjectId;
-
-/**
- * A snapshot of a leader and its view of the world.
- */
-public class LeaderSnapshot {
- final List<ReplicaSnapshot> replicas = new ArrayList<>();
- KetchLeader.State state;
- long term;
- LogIndex headIndex;
- LogIndex committedIndex;
- boolean idle;
-
- LeaderSnapshot() {
- }
-
- /**
- * Get unmodifiable view of configured replicas.
- *
- * @return unmodifiable view of configured replicas.
- */
- public Collection<ReplicaSnapshot> getReplicas() {
- return Collections.unmodifiableList(replicas);
- }
-
- /**
- * Get current state of the leader.
- *
- * @return current state of the leader.
- */
- public KetchLeader.State getState() {
- return state;
- }
-
- /**
- * Whether the leader is not running a round to reach consensus, and has no
- * rounds queued.
- *
- * @return {@code true} if the leader is not running a round to reach
- * consensus, and has no rounds queued.
- */
- public boolean isIdle() {
- return idle;
- }
-
- /**
- * Get term of this leader
- *
- * @return term of this leader. Valid only if {@link #getState()} is
- * currently
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader.State#LEADER}.
- */
- public long getTerm() {
- return term;
- }
-
- /**
- * Get end of the leader's log
- *
- * @return end of the leader's log; null if leader hasn't started up enough
- * to begin its own election.
- */
- @Nullable
- public LogIndex getHead() {
- return headIndex;
- }
-
- /**
- * Get state the leader knows is committed on a majority of participant
- * replicas
- *
- * @return state the leader knows is committed on a majority of participant
- * replicas. Null until the leader instance has committed a log
- * index within its own term.
- */
- @Nullable
- public LogIndex getCommitted() {
- return committedIndex;
- }
-
- /** {@inheritDoc} */
- @Override
- public String toString() {
- StringBuilder s = new StringBuilder();
- s.append(isIdle() ? "IDLE" : "RUNNING"); //$NON-NLS-1$ //$NON-NLS-2$
- s.append(" state ").append(getState()); //$NON-NLS-1$
- if (getTerm() > 0) {
- s.append(" term ").append(getTerm()); //$NON-NLS-1$
- }
- s.append('\n');
- s.append(String.format(
- "%-10s %12s %12s\n", //$NON-NLS-1$
- "Replica", "Accepted", "Committed")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- s.append("------------------------------------\n"); //$NON-NLS-1$
- debug(s, "(leader)", getHead(), getCommitted()); //$NON-NLS-1$
- s.append('\n');
- for (ReplicaSnapshot r : getReplicas()) {
- debug(s, r);
- s.append('\n');
- }
- s.append('\n');
- return s.toString();
- }
-
- private static void debug(StringBuilder b, ReplicaSnapshot s) {
- KetchReplica replica = s.getReplica();
- debug(b, replica.getName(), s.getAccepted(), s.getCommitted());
- b.append(String.format(" %-8s %s", //$NON-NLS-1$
- replica.getParticipation(), s.getState()));
- if (s.getState() == OFFLINE) {
- String err = s.getErrorMessage();
- if (err != null) {
- b.append(" (").append(err).append(')'); //$NON-NLS-1$
- }
- }
- }
-
- private static void debug(StringBuilder s, String name,
- ObjectId accepted, ObjectId committed) {
- s.append(String.format(
- "%-10s %-12s %-12s", //$NON-NLS-1$
- name, str(accepted), str(committed)));
- }
-
- static String str(ObjectId c) {
- if (c instanceof LogIndex) {
- return ((LogIndex) c).describeForLog();
- } else if (c != null) {
- return c.abbreviate(8).name();
- }
- return "-"; //$NON-NLS-1$
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java
deleted file mode 100644
index b2d59d77da..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static org.eclipse.jgit.internal.ketch.KetchReplica.CommitMethod.ALL_REFS;
-import static org.eclipse.jgit.internal.ketch.KetchReplica.CommitMethod.TXN_COMMITTED;
-import static org.eclipse.jgit.lib.RefDatabase.ALL;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
-
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.jgit.internal.storage.reftree.RefTreeDatabase;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDatabase;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.util.time.MonotonicClock;
-import org.eclipse.jgit.util.time.ProposedTimestamp;
-
-/**
- * Ketch replica running on the same system as the
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader}.
- */
-public class LocalReplica extends KetchReplica {
- /**
- * Configure a local replica.
- *
- * @param leader
- * instance this replica follows.
- * @param name
- * unique-ish name identifying this replica for debugging.
- * @param cfg
- * how Ketch should treat the local system.
- */
- public LocalReplica(KetchLeader leader, String name, ReplicaConfig cfg) {
- super(leader, name, cfg);
- }
-
- /** {@inheritDoc} */
- @Override
- protected String describeForLog() {
- return String.format("%s (leader)", getName()); //$NON-NLS-1$
- }
-
- /**
- * Initializes local replica by reading accepted and committed references.
- * <p>
- * Loads accepted and committed references from the reference database of
- * the local replica and stores their current ObjectIds in memory.
- *
- * @param repo
- * repository to initialize state from.
- * @throws IOException
- * cannot read repository state.
- */
- void initialize(Repository repo) throws IOException {
- RefDatabase refdb = repo.getRefDatabase();
- if (refdb instanceof RefTreeDatabase) {
- RefTreeDatabase treeDb = (RefTreeDatabase) refdb;
- String txnNamespace = getSystem().getTxnNamespace();
- if (!txnNamespace.equals(treeDb.getTxnNamespace())) {
- throw new IOException(MessageFormat.format(
- KetchText.get().mismatchedTxnNamespace,
- txnNamespace, treeDb.getTxnNamespace()));
- }
- refdb = treeDb.getBootstrap();
- }
- initialize(refdb.exactRef(
- getSystem().getTxnAccepted(),
- getSystem().getTxnCommitted()));
- }
-
- /** {@inheritDoc} */
- @Override
- protected void startPush(ReplicaPushRequest req) {
- getSystem().getExecutor().execute(() -> {
- MonotonicClock clk = getSystem().getClock();
- try (Repository git = getLeader().openRepository();
- ProposedTimestamp ts = clk.propose()) {
- try {
- update(git, req, ts);
- req.done(git);
- } catch (Throwable err) {
- req.setException(git, err);
- }
- } catch (IOException err) {
- req.setException(null, err);
- }
- });
- }
-
- /** {@inheritDoc} */
- @Override
- protected void blockingFetch(Repository repo, ReplicaFetchRequest req)
- throws IOException {
- throw new IOException(KetchText.get().cannotFetchFromLocalReplica);
- }
-
- private void update(Repository git, ReplicaPushRequest req,
- ProposedTimestamp ts) throws IOException {
- RefDatabase refdb = git.getRefDatabase();
- CommitMethod method = getCommitMethod();
-
- // Local replica probably uses RefTreeDatabase, the request should
- // be only for the txnNamespace, so drop to the bootstrap layer.
- if (refdb instanceof RefTreeDatabase) {
- if (!isOnlyTxnNamespace(req.getCommands())) {
- return;
- }
-
- refdb = ((RefTreeDatabase) refdb).getBootstrap();
- method = TXN_COMMITTED;
- }
-
- BatchRefUpdate batch = refdb.newBatchUpdate();
- batch.addProposedTimestamp(ts);
- batch.setRefLogIdent(getSystem().newCommitter(ts));
- batch.setRefLogMessage("ketch", false); //$NON-NLS-1$
- batch.setAllowNonFastForwards(true);
-
- // RefDirectory updates multiple references sequentially.
- // Run everything else first, then accepted (if present),
- // then committed (if present). This ensures an earlier
- // failure will not update these critical references.
- ReceiveCommand accepted = null;
- ReceiveCommand committed = null;
- for (ReceiveCommand cmd : req.getCommands()) {
- String name = cmd.getRefName();
- if (name.equals(getSystem().getTxnAccepted())) {
- accepted = cmd;
- } else if (name.equals(getSystem().getTxnCommitted())) {
- committed = cmd;
- } else {
- batch.addCommand(cmd);
- }
- }
- if (committed != null && method == ALL_REFS) {
- Map<String, Ref> refs = refdb.getRefs(ALL);
- batch.addCommand(prepareCommit(git, refs, committed.getNewId()));
- }
- if (accepted != null) {
- batch.addCommand(accepted);
- }
- if (committed != null) {
- batch.addCommand(committed);
- }
-
- try (RevWalk rw = new RevWalk(git)) {
- batch.execute(rw, NullProgressMonitor.INSTANCE);
- }
-
- // KetchReplica only cares about accepted and committed in
- // advertisement. If they failed, store the current values
- // back in the ReplicaPushRequest.
- List<String> failed = new ArrayList<>(2);
- checkFailed(failed, accepted);
- checkFailed(failed, committed);
- if (!failed.isEmpty()) {
- String[] arr = failed.toArray(new String[0]);
- req.setRefs(refdb.exactRef(arr));
- }
- }
-
- private static void checkFailed(List<String> failed, ReceiveCommand cmd) {
- if (cmd != null && cmd.getResult() != OK) {
- failed.add(cmd.getRefName());
- }
- }
-
- private boolean isOnlyTxnNamespace(Collection<ReceiveCommand> cmdList) {
- // Be paranoid and reject non txnNamespace names, this
- // is a programming error in Ketch that should not occur.
-
- String txnNamespace = getSystem().getTxnNamespace();
- for (ReceiveCommand cmd : cmdList) {
- if (!cmd.getRefName().startsWith(txnNamespace)) {
- cmd.setResult(REJECTED_OTHER_REASON,
- MessageFormat.format(
- KetchText.get().outsideTxnNamespace,
- cmd.getRefName(), txnNamespace));
- ReceiveCommand.abort(cmdList);
- return false;
- }
- }
- return true;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LogIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LogIndex.java
deleted file mode 100644
index ed65c06fae..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LogIndex.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.ObjectId;
-
-/**
- * An ObjectId for a commit extended with incrementing log index.
- * <p>
- * For any two LogIndex instances, {@code A} is an ancestor of {@code C}
- * reachable through parent edges in the graph if {@code A.index < C.index}.
- * LogIndex provides a performance optimization for Ketch, the same information
- * can be obtained from {@link org.eclipse.jgit.revwalk.RevWalk}.
- * <p>
- * Index values are only valid within a single
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader} instance after it has won
- * an election. By restricting scope to a single leader new leaders do not need
- * to traverse the entire history to determine the next {@code index} for new
- * proposals. This differs from Raft, where leader election uses the log index
- * and the term number to determine which replica holds a sufficiently
- * up-to-date log. Since Ketch uses Git objects for storage of its replicated
- * log, it keeps the term number as Raft does but uses standard Git operations
- * to imply the log index.
- * <p>
- * {@link org.eclipse.jgit.internal.ketch.Round#runAsync(AnyObjectId)} bumps the
- * index as each new round is constructed.
- */
-public class LogIndex extends ObjectId {
- static LogIndex unknown(AnyObjectId id) {
- return new LogIndex(id, 0);
- }
-
- private final long index;
-
- private LogIndex(AnyObjectId id, long index) {
- super(id);
- this.index = index;
- }
-
- LogIndex nextIndex(AnyObjectId id) {
- return new LogIndex(id, index + 1);
- }
-
- /**
- * Get index provided by the current leader instance.
- *
- * @return index provided by the current leader instance.
- */
- public long getIndex() {
- return index;
- }
-
- /**
- * Check if this log position committed before another log position.
- * <p>
- * Only valid for log positions in memory for the current leader.
- *
- * @param c
- * other (more recent) log position.
- * @return true if this log position was before {@code c} or equal to c and
- * therefore any agreement of {@code c} implies agreement on this
- * log position.
- */
- boolean isBefore(LogIndex c) {
- return index <= c.index;
- }
-
- /**
- * Create string suitable for debug logging containing the log index and
- * abbreviated ObjectId.
- *
- * @return string suitable for debug logging containing the log index and
- * abbreviated ObjectId.
- */
- @SuppressWarnings("boxing")
- public String describeForLog() {
- return String.format("%5d/%s", index, abbreviate(6).name()); //$NON-NLS-1$
- }
-
- /** {@inheritDoc} */
- @SuppressWarnings("boxing")
- @Override
- public String toString() {
- return String.format("LogId[%5d/%s]", index, name()); //$NON-NLS-1$
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Proposal.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Proposal.java
deleted file mode 100644
index ca27281a8e..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Proposal.java
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static org.eclipse.jgit.internal.ketch.Proposal.State.ABORTED;
-import static org.eclipse.jgit.internal.ketch.Proposal.State.EXECUTED;
-import static org.eclipse.jgit.internal.ketch.Proposal.State.NEW;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.internal.storage.reftree.Command;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.PushCertificate;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.util.time.ProposedTimestamp;
-
-/**
- * A proposal to be applied in a Ketch system.
- * <p>
- * Pushing to a Ketch leader results in the leader making a proposal. The
- * proposal includes the list of reference updates. The leader attempts to send
- * the proposal to a quorum of replicas by pushing the proposal to a "staging"
- * area under the {@code refs/txn/stage/} namespace. If the proposal succeeds
- * then the changes are durable and the leader can commit the proposal.
- * <p>
- * Proposals are executed by
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader#queueProposal(Proposal)},
- * which runs them asynchronously in the background. Proposals are thread-safe
- * futures allowing callers to {@link #await()} for results or be notified by
- * callback using {@link #addListener(Runnable)}.
- */
-public class Proposal {
- /** Current state of the proposal. */
- public enum State {
- /** Proposal has not yet been given to a {@link KetchLeader}. */
- NEW(false),
-
- /**
- * Proposal was validated and has entered the queue, but a round
- * containing this proposal has not started yet.
- */
- QUEUED(false),
-
- /** Round containing the proposal has begun and is in progress. */
- RUNNING(false),
-
- /**
- * Proposal was executed through a round. Individual results from
- * {@link Proposal#getCommands()}, {@link Command#getResult()} explain
- * the success or failure outcome.
- */
- EXECUTED(true),
-
- /** Proposal was aborted and did not reach consensus. */
- ABORTED(true);
-
- private final boolean done;
-
- private State(boolean done) {
- this.done = done;
- }
-
- /** @return true if this is a terminal state. */
- public boolean isDone() {
- return done;
- }
- }
-
- private final List<Command> commands;
- private PersonIdent author;
- private String message;
- private PushCertificate pushCert;
-
- private List<ProposedTimestamp> timestamps;
- private final List<Runnable> listeners = new CopyOnWriteArrayList<>();
- private final AtomicReference<State> state = new AtomicReference<>(NEW);
-
- /**
- * Create a proposal from a list of Ketch commands.
- *
- * @param cmds
- * prepared list of commands.
- */
- public Proposal(List<Command> cmds) {
- commands = Collections.unmodifiableList(new ArrayList<>(cmds));
- }
-
- /**
- * Create a proposal from a collection of received commands.
- *
- * @param rw
- * walker to assist in preparing commands.
- * @param cmds
- * list of pending commands.
- * @throws org.eclipse.jgit.errors.MissingObjectException
- * newId of a command is not found locally.
- * @throws java.io.IOException
- * local objects cannot be accessed.
- */
- public Proposal(RevWalk rw, Collection<ReceiveCommand> cmds)
- throws MissingObjectException, IOException {
- commands = asCommandList(rw, cmds);
- }
-
- private static List<Command> asCommandList(RevWalk rw,
- Collection<ReceiveCommand> cmds)
- throws MissingObjectException, IOException {
- List<Command> commands = new ArrayList<>(cmds.size());
- for (ReceiveCommand cmd : cmds) {
- commands.add(new Command(rw, cmd));
- }
- return Collections.unmodifiableList(commands);
- }
-
- /**
- * Get commands from this proposal.
- *
- * @return commands from this proposal.
- */
- public Collection<Command> getCommands() {
- return commands;
- }
-
- /**
- * Get optional author of the proposal.
- *
- * @return optional author of the proposal.
- */
- @Nullable
- public PersonIdent getAuthor() {
- return author;
- }
-
- /**
- * Set the author for the proposal.
- *
- * @param who
- * optional identity of the author of the proposal.
- * @return {@code this}
- */
- public Proposal setAuthor(@Nullable PersonIdent who) {
- author = who;
- return this;
- }
-
- /**
- * Get optional message for the commit log of the RefTree.
- *
- * @return optional message for the commit log of the RefTree.
- */
- @Nullable
- public String getMessage() {
- return message;
- }
-
- /**
- * Set the message to appear in the commit log of the RefTree.
- *
- * @param msg
- * message text for the commit.
- * @return {@code this}
- */
- public Proposal setMessage(@Nullable String msg) {
- message = msg != null && !msg.isEmpty() ? msg : null;
- return this;
- }
-
- /**
- * Get optional certificate signing the references.
- *
- * @return optional certificate signing the references.
- */
- @Nullable
- public PushCertificate getPushCertificate() {
- return pushCert;
- }
-
- /**
- * Set the push certificate signing the references.
- *
- * @param cert
- * certificate, may be null.
- * @return {@code this}
- */
- public Proposal setPushCertificate(@Nullable PushCertificate cert) {
- pushCert = cert;
- return this;
- }
-
- /**
- * Get timestamps that Ketch must block for.
- *
- * @return timestamps that Ketch must block for. These may have been used as
- * commit times inside the objects involved in the proposal.
- */
- public List<ProposedTimestamp> getProposedTimestamps() {
- if (timestamps != null) {
- return timestamps;
- }
- return Collections.emptyList();
- }
-
- /**
- * Request the proposal to wait for the affected timestamps to resolve.
- *
- * @param ts
- * a {@link org.eclipse.jgit.util.time.ProposedTimestamp} object.
- * @return {@code this}.
- */
- public Proposal addProposedTimestamp(ProposedTimestamp ts) {
- if (timestamps == null) {
- timestamps = new ArrayList<>(4);
- }
- timestamps.add(ts);
- return this;
- }
-
- /**
- * Add a callback to be invoked when the proposal is done.
- * <p>
- * A proposal is done when it has entered either
- * {@link org.eclipse.jgit.internal.ketch.Proposal.State#EXECUTED} or
- * {@link org.eclipse.jgit.internal.ketch.Proposal.State#ABORTED} state. If
- * the proposal is already done {@code callback.run()} is immediately
- * invoked on the caller's thread.
- *
- * @param callback
- * method to run after the proposal is done. The callback may be
- * run on a Ketch system thread and should be completed quickly.
- */
- public void addListener(Runnable callback) {
- boolean runNow = false;
- synchronized (state) {
- if (state.get().isDone()) {
- runNow = true;
- } else {
- listeners.add(callback);
- }
- }
- if (runNow) {
- callback.run();
- }
- }
-
- /** Set command result as OK. */
- void success() {
- for (Command c : commands) {
- if (c.getResult() == NOT_ATTEMPTED) {
- c.setResult(OK);
- }
- }
- notifyState(EXECUTED);
- }
-
- /** Mark commands as "transaction aborted". */
- void abort() {
- Command.abort(commands, null);
- notifyState(ABORTED);
- }
-
- /**
- * Read the current state of the proposal.
- *
- * @return read the current state of the proposal.
- */
- public State getState() {
- return state.get();
- }
-
- /**
- * Whether the proposal was attempted
- *
- * @return {@code true} if the proposal was attempted. A true value does not
- * mean consensus was reached, only that the proposal was considered
- * and will not be making any more progress beyond its current
- * state.
- */
- public boolean isDone() {
- return state.get().isDone();
- }
-
- /**
- * Wait for the proposal to be attempted and {@link #isDone()} to be true.
- *
- * @throws java.lang.InterruptedException
- * caller was interrupted before proposal executed.
- */
- public void await() throws InterruptedException {
- synchronized (state) {
- while (!state.get().isDone()) {
- state.wait();
- }
- }
- }
-
- /**
- * Wait for the proposal to be attempted and {@link #isDone()} to be true.
- *
- * @param wait
- * how long to wait.
- * @param unit
- * unit describing the wait time.
- * @return true if the proposal is done; false if the method timed out.
- * @throws java.lang.InterruptedException
- * caller was interrupted before proposal executed.
- */
- public boolean await(long wait, TimeUnit unit) throws InterruptedException {
- synchronized (state) {
- if (state.get().isDone()) {
- return true;
- }
- state.wait(unit.toMillis(wait));
- return state.get().isDone();
- }
- }
-
- /**
- * Wait for the proposal to exit a state.
- *
- * @param notIn
- * state the proposal should not be in to return.
- * @param wait
- * how long to wait.
- * @param unit
- * unit describing the wait time.
- * @return true if the proposal exited the state; false on time out.
- * @throws java.lang.InterruptedException
- * caller was interrupted before proposal executed.
- */
- public boolean awaitStateChange(State notIn, long wait, TimeUnit unit)
- throws InterruptedException {
- synchronized (state) {
- if (state.get() != notIn) {
- return true;
- }
- state.wait(unit.toMillis(wait));
- return state.get() != notIn;
- }
- }
-
- void notifyState(State s) {
- synchronized (state) {
- state.set(s);
- state.notifyAll();
- }
- if (s.isDone()) {
- for (Runnable callback : listeners) {
- callback.run();
- }
- listeners.clear();
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public String toString() {
- StringBuilder s = new StringBuilder();
- s.append("Ketch Proposal {\n"); //$NON-NLS-1$
- s.append(" ").append(state.get()).append('\n'); //$NON-NLS-1$
- if (author != null) {
- s.append(" author ").append(author).append('\n'); //$NON-NLS-1$
- }
- if (message != null) {
- s.append(" message ").append(message).append('\n'); //$NON-NLS-1$
- }
- for (Command c : commands) {
- s.append(" "); //$NON-NLS-1$
- format(s, c.getOldRef(), "CREATE"); //$NON-NLS-1$
- s.append(' ');
- format(s, c.getNewRef(), "DELETE"); //$NON-NLS-1$
- s.append(' ').append(c.getRefName());
- if (c.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED) {
- s.append(' ').append(c.getResult()); // $NON-NLS-1$
- }
- s.append('\n');
- }
- s.append('}');
- return s.toString();
- }
-
- private static void format(StringBuilder s, @Nullable Ref r, String n) {
- if (r == null) {
- s.append(n);
- } else if (r.isSymbolic()) {
- s.append(r.getTarget().getName());
- } else {
- ObjectId id = r.getObjectId();
- if (id != null) {
- s.append(id.abbreviate(8).name());
- }
- }
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ProposalRound.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ProposalRound.java
deleted file mode 100644
index b73183abd0..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ProposalRound.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static org.eclipse.jgit.internal.ketch.Proposal.State.RUNNING;
-
-import java.io.IOException;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeoutException;
-import java.util.stream.Collectors;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.internal.storage.reftree.Command;
-import org.eclipse.jgit.internal.storage.reftree.RefTree;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.util.time.ProposedTimestamp;
-
-/** A {@link Round} that aggregates and sends user {@link Proposal}s. */
-class ProposalRound extends Round {
- private final List<Proposal> todo;
- private RefTree queuedTree;
-
- ProposalRound(KetchLeader leader, LogIndex head, List<Proposal> todo,
- @Nullable RefTree tree) {
- super(leader, head);
- this.todo = todo;
-
- if (tree != null && canCombine(todo)) {
- this.queuedTree = tree;
- } else {
- leader.roundHoldsReferenceToRefTree = false;
- }
- }
-
- private static boolean canCombine(List<Proposal> todo) {
- Proposal first = todo.get(0);
- for (int i = 1; i < todo.size(); i++) {
- if (!canCombine(first, todo.get(i))) {
- return false;
- }
- }
- return true;
- }
-
- private static boolean canCombine(Proposal a, Proposal b) {
- String aMsg = nullToEmpty(a.getMessage());
- String bMsg = nullToEmpty(b.getMessage());
- return aMsg.equals(bMsg) && canCombine(a.getAuthor(), b.getAuthor());
- }
-
- private static String nullToEmpty(@Nullable String str) {
- return str != null ? str : ""; //$NON-NLS-1$
- }
-
- private static boolean canCombine(@Nullable PersonIdent a,
- @Nullable PersonIdent b) {
- if (a != null && b != null) {
- // Same name and email address. Combine timestamp as the two
- // proposals are running concurrently and appear together or
- // not at all from the point of view of an outside reader.
- return a.getName().equals(b.getName())
- && a.getEmailAddress().equals(b.getEmailAddress());
- }
-
- // If a and b are null, both will be the system identity.
- return a == null && b == null;
- }
-
- @Override
- void start() throws IOException {
- for (Proposal p : todo) {
- p.notifyState(RUNNING);
- }
- try {
- ObjectId id;
- try (Repository git = leader.openRepository();
- ProposedTimestamp ts = getSystem().getClock().propose()) {
- id = insertProposals(git, ts);
- blockUntil(ts);
- }
- runAsync(id);
- } catch (NoOp e) {
- for (Proposal p : todo) {
- p.success();
- }
- leader.lock.lock();
- try {
- leader.nextRound();
- } finally {
- leader.lock.unlock();
- }
- } catch (IOException e) {
- abort();
- throw e;
- }
- }
-
- private ObjectId insertProposals(Repository git, ProposedTimestamp ts)
- throws IOException, NoOp {
- ObjectId id;
- try (ObjectInserter inserter = git.newObjectInserter()) {
- // TODO(sop) Process signed push certificates.
-
- if (queuedTree != null) {
- id = insertSingleProposal(git, ts, inserter);
- } else {
- id = insertMultiProposal(git, ts, inserter);
- }
-
- stageCommands = makeStageList(git, inserter);
- inserter.flush();
- }
- return id;
- }
-
- private ObjectId insertSingleProposal(Repository git, ProposedTimestamp ts,
- ObjectInserter inserter) throws IOException, NoOp {
- // Fast path: tree is passed in with all proposals applied.
- ObjectId treeId = queuedTree.writeTree(inserter);
- queuedTree = null;
- leader.roundHoldsReferenceToRefTree = false;
-
- if (!ObjectId.zeroId().equals(acceptedOldIndex)) {
- try (RevWalk rw = new RevWalk(git)) {
- RevCommit c = rw.parseCommit(acceptedOldIndex);
- if (treeId.equals(c.getTree())) {
- throw new NoOp();
- }
- }
- }
-
- Proposal p = todo.get(0);
- CommitBuilder b = new CommitBuilder();
- b.setTreeId(treeId);
- if (!ObjectId.zeroId().equals(acceptedOldIndex)) {
- b.setParentId(acceptedOldIndex);
- }
- b.setCommitter(leader.getSystem().newCommitter(ts));
- b.setAuthor(p.getAuthor() != null ? p.getAuthor() : b.getCommitter());
- b.setMessage(message(p));
- return inserter.insert(b);
- }
-
- private ObjectId insertMultiProposal(Repository git, ProposedTimestamp ts,
- ObjectInserter inserter) throws IOException, NoOp {
- // The tree was not passed in, or there are multiple proposals
- // each needing their own commit. Reset the tree and replay each
- // proposal in order as individual commits.
- ObjectId lastIndex = acceptedOldIndex;
- ObjectId oldTreeId;
- RefTree tree;
- if (ObjectId.zeroId().equals(lastIndex)) {
- oldTreeId = ObjectId.zeroId();
- tree = RefTree.newEmptyTree();
- } else {
- try (RevWalk rw = new RevWalk(git)) {
- RevCommit c = rw.parseCommit(lastIndex);
- oldTreeId = c.getTree();
- tree = RefTree.read(rw.getObjectReader(), c.getTree());
- }
- }
-
- PersonIdent committer = leader.getSystem().newCommitter(ts);
- for (Proposal p : todo) {
- if (!tree.apply(p.getCommands())) {
- // This should not occur, previously during queuing the
- // commands were successfully applied to the pending tree.
- // Abort the entire round.
- throw new IOException(
- KetchText.get().queuedProposalFailedToApply);
- }
-
- ObjectId treeId = tree.writeTree(inserter);
- if (treeId.equals(oldTreeId)) {
- continue;
- }
-
- CommitBuilder b = new CommitBuilder();
- b.setTreeId(treeId);
- if (!ObjectId.zeroId().equals(lastIndex)) {
- b.setParentId(lastIndex);
- }
- b.setAuthor(p.getAuthor() != null ? p.getAuthor() : committer);
- b.setCommitter(committer);
- b.setMessage(message(p));
- lastIndex = inserter.insert(b);
- }
- if (lastIndex.equals(acceptedOldIndex)) {
- throw new NoOp();
- }
- return lastIndex;
- }
-
- private String message(Proposal p) {
- StringBuilder m = new StringBuilder();
- String msg = p.getMessage();
- if (msg != null && !msg.isEmpty()) {
- m.append(msg);
- while (m.length() < 2 || m.charAt(m.length() - 2) != '\n'
- || m.charAt(m.length() - 1) != '\n') {
- m.append('\n');
- }
- }
- m.append(KetchConstants.TERM.getName())
- .append(": ") //$NON-NLS-1$
- .append(leader.getTerm());
- return m.toString();
- }
-
- void abort() {
- for (Proposal p : todo) {
- p.abort();
- }
- }
-
- @Override
- void success() {
- for (Proposal p : todo) {
- p.success();
- }
- }
-
- private List<ReceiveCommand> makeStageList(Repository git,
- ObjectInserter inserter) throws IOException {
- // For each branch, collapse consecutive updates to only most recent,
- // avoiding sending multiple objects in a rapid fast-forward chain, or
- // rewritten content.
- Map<String, ObjectId> byRef = new HashMap<>();
- for (Proposal p : todo) {
- for (Command c : p.getCommands()) {
- Ref n = c.getNewRef();
- if (n != null && !n.isSymbolic()) {
- byRef.put(n.getName(), n.getObjectId());
- }
- }
- }
- if (byRef.isEmpty()) {
- return Collections.emptyList();
- }
-
- Set<ObjectId> newObjs = new HashSet<>(byRef.values());
- StageBuilder b = new StageBuilder(
- leader.getSystem().getTxnStage(),
- acceptedNewIndex);
- return b.makeStageList(newObjs, git, inserter);
- }
-
- private void blockUntil(ProposedTimestamp ts)
- throws TimeIsUncertainException {
- List<ProposedTimestamp> times = todo.stream()
- .flatMap(p -> p.getProposedTimestamps().stream())
- .collect(Collectors.toCollection(ArrayList::new));
- times.add(ts);
-
- try {
- Duration maxWait = getSystem().getMaxWaitForMonotonicClock();
- ProposedTimestamp.blockUntil(times, maxWait);
- } catch (InterruptedException | TimeoutException e) {
- throw new TimeIsUncertainException(e);
- }
- }
-
- private static class NoOp extends Exception {
- private static final long serialVersionUID = 1L;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java
deleted file mode 100644
index fac93c84b3..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static org.eclipse.jgit.internal.ketch.KetchReplica.CommitMethod.ALL_REFS;
-import static org.eclipse.jgit.lib.Ref.Storage.NETWORK;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.LOCK_FAILURE;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NODELETE;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.errors.NotSupportedException;
-import org.eclipse.jgit.errors.TransportException;
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.FetchConnection;
-import org.eclipse.jgit.transport.PushConnection;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.transport.RemoteConfig;
-import org.eclipse.jgit.transport.RemoteRefUpdate;
-import org.eclipse.jgit.transport.Transport;
-import org.eclipse.jgit.transport.URIish;
-
-/**
- * Representation of a Git repository on a remote replica system.
- * <p>
- * {@link org.eclipse.jgit.internal.ketch.KetchLeader} will contact the replica
- * using the Git wire protocol.
- * <p>
- * The remote replica may be fully Ketch-aware, or a standard Git server.
- */
-public class RemoteGitReplica extends KetchReplica {
- private final URIish uri;
- private final RemoteConfig remoteConfig;
-
- /**
- * Configure a new remote.
- *
- * @param leader
- * instance this replica follows.
- * @param name
- * unique-ish name identifying this remote for debugging.
- * @param uri
- * URI to connect to the follower's repository.
- * @param cfg
- * how Ketch should treat the remote system.
- * @param rc
- * optional remote configuration describing how to contact the
- * peer repository.
- */
- public RemoteGitReplica(KetchLeader leader, String name, URIish uri,
- ReplicaConfig cfg, @Nullable RemoteConfig rc) {
- super(leader, name, cfg);
- this.uri = uri;
- this.remoteConfig = rc;
- }
-
- /**
- * Get URI to contact the remote peer repository.
- *
- * @return URI to contact the remote peer repository.
- */
- public URIish getURI() {
- return uri;
- }
-
- /**
- * Get optional configuration describing how to contact the peer.
- *
- * @return optional configuration describing how to contact the peer.
- */
- @Nullable
- protected RemoteConfig getRemoteConfig() {
- return remoteConfig;
- }
-
- /** {@inheritDoc} */
- @Override
- protected String describeForLog() {
- return String.format("%s @ %s", getName(), getURI()); //$NON-NLS-1$
- }
-
- /** {@inheritDoc} */
- @Override
- protected void startPush(ReplicaPushRequest req) {
- getSystem().getExecutor().execute(() -> {
- try (Repository git = getLeader().openRepository()) {
- try {
- push(git, req);
- req.done(git);
- } catch (Throwable err) {
- req.setException(git, err);
- }
- } catch (IOException err) {
- req.setException(null, err);
- }
- });
- }
-
- private void push(Repository repo, ReplicaPushRequest req)
- throws NotSupportedException, TransportException, IOException {
- Map<String, Ref> adv;
- List<RemoteCommand> cmds = asUpdateList(req.getCommands());
- try (Transport transport = Transport.open(repo, uri)) {
- RemoteConfig rc = getRemoteConfig();
- if (rc != null) {
- transport.applyConfig(rc);
- }
- transport.setPushAtomic(true);
- adv = push(repo, transport, cmds);
- }
- for (RemoteCommand c : cmds) {
- c.copyStatusToResult();
- }
- req.setRefs(adv);
- }
-
- private Map<String, Ref> push(Repository git, Transport transport,
- List<RemoteCommand> cmds) throws IOException {
- Map<String, RemoteRefUpdate> updates = asUpdateMap(cmds);
- try (PushConnection connection = transport.openPush()) {
- Map<String, Ref> adv = connection.getRefsMap();
- RemoteRefUpdate accepted = updates.get(getSystem().getTxnAccepted());
- if (accepted != null && !isExpectedValue(adv, accepted)) {
- abort(cmds);
- return adv;
- }
-
- RemoteRefUpdate committed = updates.get(getSystem().getTxnCommitted());
- if (committed != null && !isExpectedValue(adv, committed)) {
- abort(cmds);
- return adv;
- }
- if (committed != null && getCommitMethod() == ALL_REFS) {
- prepareCommit(git, cmds, updates, adv,
- committed.getNewObjectId());
- }
-
- connection.push(NullProgressMonitor.INSTANCE, updates);
- return adv;
- }
- }
-
- private static boolean isExpectedValue(Map<String, Ref> adv,
- RemoteRefUpdate u) {
- Ref r = adv.get(u.getRemoteName());
- if (!AnyObjectId.isEqual(getId(r), u.getExpectedOldObjectId())) {
- ((RemoteCommand) u).cmd.setResult(LOCK_FAILURE);
- return false;
- }
- return true;
- }
-
- private void prepareCommit(Repository git, List<RemoteCommand> cmds,
- Map<String, RemoteRefUpdate> updates, Map<String, Ref> adv,
- ObjectId committed) throws IOException {
- for (ReceiveCommand cmd : prepareCommit(git, adv, committed)) {
- RemoteCommand c = new RemoteCommand(cmd);
- cmds.add(c);
- updates.put(c.getRemoteName(), c);
- }
- }
-
- private static List<RemoteCommand> asUpdateList(
- Collection<ReceiveCommand> cmds) {
- try {
- List<RemoteCommand> toPush = new ArrayList<>(cmds.size());
- for (ReceiveCommand cmd : cmds) {
- toPush.add(new RemoteCommand(cmd));
- }
- return toPush;
- } catch (IOException e) {
- // Cannot occur as no IO was required to build the command.
- throw new IllegalStateException(e);
- }
- }
-
- private static Map<String, RemoteRefUpdate> asUpdateMap(
- List<RemoteCommand> cmds) {
- Map<String, RemoteRefUpdate> m = new LinkedHashMap<>();
- for (RemoteCommand cmd : cmds) {
- m.put(cmd.getRemoteName(), cmd);
- }
- return m;
- }
-
- private static void abort(List<RemoteCommand> cmds) {
- List<ReceiveCommand> tmp = new ArrayList<>(cmds.size());
- for (RemoteCommand cmd : cmds) {
- tmp.add(cmd.cmd);
- }
- ReceiveCommand.abort(tmp);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void blockingFetch(Repository repo, ReplicaFetchRequest req)
- throws NotSupportedException, TransportException {
- try (Transport transport = Transport.open(repo, uri)) {
- RemoteConfig rc = getRemoteConfig();
- if (rc != null) {
- transport.applyConfig(rc);
- }
- fetch(transport, req);
- }
- }
-
- private void fetch(Transport transport, ReplicaFetchRequest req)
- throws NotSupportedException, TransportException {
- try (FetchConnection conn = transport.openFetch()) {
- Map<String, Ref> remoteRefs = conn.getRefsMap();
- req.setRefs(remoteRefs);
-
- List<Ref> want = new ArrayList<>();
- for (String name : req.getWantRefs()) {
- Ref ref = remoteRefs.get(name);
- if (ref != null && ref.getObjectId() != null) {
- want.add(ref);
- }
- }
- for (ObjectId id : req.getWantObjects()) {
- want.add(new ObjectIdRef.Unpeeled(NETWORK, id.name(), id));
- }
-
- conn.fetch(NullProgressMonitor.INSTANCE, want,
- Collections.<ObjectId> emptySet());
- }
- }
-
- static class RemoteCommand extends RemoteRefUpdate {
- final ReceiveCommand cmd;
-
- RemoteCommand(ReceiveCommand cmd) throws IOException {
- super(null, null,
- cmd.getNewId(), cmd.getRefName(),
- true /* force update */,
- null /* no local tracking ref */,
- cmd.getOldId());
- this.cmd = cmd;
- }
-
- void copyStatusToResult() {
- if (cmd.getResult() == NOT_ATTEMPTED) {
- switch (getStatus()) {
- case OK:
- case UP_TO_DATE:
- case NON_EXISTING:
- cmd.setResult(OK);
- break;
-
- case REJECTED_NODELETE:
- cmd.setResult(REJECTED_NODELETE);
- break;
-
- case REJECTED_NONFASTFORWARD:
- cmd.setResult(REJECTED_NONFASTFORWARD);
- break;
-
- case REJECTED_OTHER_REASON:
- cmd.setResult(REJECTED_OTHER_REASON, getMessage());
- break;
-
- default:
- cmd.setResult(REJECTED_OTHER_REASON, getStatus().name());
- break;
- }
- }
- }
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaConfig.java
deleted file mode 100644
index 1d323b8490..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaConfig.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static java.util.concurrent.TimeUnit.DAYS;
-import static java.util.concurrent.TimeUnit.HOURS;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.MINUTES;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.eclipse.jgit.internal.ketch.KetchConstants.CONFIG_KEY_COMMIT;
-import static org.eclipse.jgit.internal.ketch.KetchConstants.CONFIG_KEY_SPEED;
-import static org.eclipse.jgit.internal.ketch.KetchConstants.CONFIG_KEY_TYPE;
-import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_REMOTE;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.eclipse.jgit.internal.ketch.KetchReplica.CommitMethod;
-import org.eclipse.jgit.internal.ketch.KetchReplica.CommitSpeed;
-import org.eclipse.jgit.internal.ketch.KetchReplica.Participation;
-import org.eclipse.jgit.lib.Config;
-
-/**
- * Configures a {@link org.eclipse.jgit.internal.ketch.KetchReplica}.
- */
-public class ReplicaConfig {
- /**
- * Read a configuration from a config block.
- *
- * @param cfg
- * configuration to read.
- * @param name
- * of the replica being configured.
- * @return replica configuration for {@code name}.
- */
- public static ReplicaConfig newFromConfig(Config cfg, String name) {
- return new ReplicaConfig().fromConfig(cfg, name);
- }
-
- private Participation participation = Participation.FULL;
- private CommitMethod commitMethod = CommitMethod.ALL_REFS;
- private CommitSpeed commitSpeed = CommitSpeed.BATCHED;
- private long minRetry = SECONDS.toMillis(5);
- private long maxRetry = MINUTES.toMillis(1);
-
- /**
- * Get participation of the replica in the system.
- *
- * @return participation of the replica in the system.
- */
- public Participation getParticipation() {
- return participation;
- }
-
- /**
- * Get how Ketch should apply committed changes.
- *
- * @return how Ketch should apply committed changes.
- */
- public CommitMethod getCommitMethod() {
- return commitMethod;
- }
-
- /**
- * Get how quickly should Ketch commit.
- *
- * @return how quickly should Ketch commit.
- */
- public CommitSpeed getCommitSpeed() {
- return commitSpeed;
- }
-
- /**
- * Returns the minimum wait delay before retrying a failure.
- *
- * @param unit
- * to get retry delay in.
- * @return minimum delay before retrying a failure.
- */
- public long getMinRetry(TimeUnit unit) {
- return unit.convert(minRetry, MILLISECONDS);
- }
-
- /**
- * Returns the maximum wait delay before retrying a failure.
- *
- * @param unit
- * to get retry delay in.
- * @return maximum delay before retrying a failure.
- */
- public long getMaxRetry(TimeUnit unit) {
- return unit.convert(maxRetry, MILLISECONDS);
- }
-
- /**
- * Update the configuration from a config block.
- *
- * @param cfg
- * configuration to read.
- * @param name
- * of the replica being configured.
- * @return {@code this}
- */
- public ReplicaConfig fromConfig(Config cfg, String name) {
- participation = cfg.getEnum(
- CONFIG_KEY_REMOTE, name, CONFIG_KEY_TYPE,
- participation);
- commitMethod = cfg.getEnum(
- CONFIG_KEY_REMOTE, name, CONFIG_KEY_COMMIT,
- commitMethod);
- commitSpeed = cfg.getEnum(
- CONFIG_KEY_REMOTE, name, CONFIG_KEY_SPEED,
- commitSpeed);
- minRetry = getMillis(cfg, name, "ketch-minRetry", minRetry); //$NON-NLS-1$
- maxRetry = getMillis(cfg, name, "ketch-maxRetry", maxRetry); //$NON-NLS-1$
- return this;
- }
-
- private static long getMillis(Config cfg, String name, String key,
- long defaultValue) {
- String valStr = cfg.getString(CONFIG_KEY_REMOTE, name, key);
- if (valStr == null) {
- return defaultValue;
- }
-
- valStr = valStr.trim();
- if (valStr.isEmpty()) {
- return defaultValue;
- }
-
- Matcher m = UnitMap.PATTERN.matcher(valStr);
- if (!m.matches()) {
- return defaultValue;
- }
-
- String digits = m.group(1);
- String unitName = m.group(2).trim();
- TimeUnit unit = UnitMap.UNITS.get(unitName);
- if (unit == null) {
- return defaultValue;
- }
-
- try {
- if (digits.indexOf('.') == -1) {
- return unit.toMillis(Long.parseLong(digits));
- }
-
- double val = Double.parseDouble(digits);
- return (long) (val * unit.toMillis(1));
- } catch (NumberFormatException nfe) {
- return defaultValue;
- }
- }
-
- static class UnitMap {
- static final Pattern PATTERN = Pattern
- .compile("^([1-9][0-9]*(?:\\.[0-9]*)?)\\s*(.*)$"); //$NON-NLS-1$
-
- static final Map<String, TimeUnit> UNITS;
-
- static {
- Map<String, TimeUnit> m = new HashMap<>();
- TimeUnit u = MILLISECONDS;
- m.put("", u); //$NON-NLS-1$
- m.put("ms", u); //$NON-NLS-1$
- m.put("millis", u); //$NON-NLS-1$
- m.put("millisecond", u); //$NON-NLS-1$
- m.put("milliseconds", u); //$NON-NLS-1$
-
- u = SECONDS;
- m.put("s", u); //$NON-NLS-1$
- m.put("sec", u); //$NON-NLS-1$
- m.put("secs", u); //$NON-NLS-1$
- m.put("second", u); //$NON-NLS-1$
- m.put("seconds", u); //$NON-NLS-1$
-
- u = MINUTES;
- m.put("m", u); //$NON-NLS-1$
- m.put("min", u); //$NON-NLS-1$
- m.put("mins", u); //$NON-NLS-1$
- m.put("minute", u); //$NON-NLS-1$
- m.put("minutes", u); //$NON-NLS-1$
-
- u = HOURS;
- m.put("h", u); //$NON-NLS-1$
- m.put("hr", u); //$NON-NLS-1$
- m.put("hrs", u); //$NON-NLS-1$
- m.put("hour", u); //$NON-NLS-1$
- m.put("hours", u); //$NON-NLS-1$
-
- u = DAYS;
- m.put("d", u); //$NON-NLS-1$
- m.put("day", u); //$NON-NLS-1$
- m.put("days", u); //$NON-NLS-1$
-
- UNITS = Collections.unmodifiableMap(m);
- }
-
- private UnitMap() {
- }
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaFetchRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaFetchRequest.java
deleted file mode 100644
index f50ad62c80..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaFetchRequest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import java.util.Map;
-import java.util.Set;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-
-/**
- * A fetch request to obtain objects from a replica, and its result.
- */
-public class ReplicaFetchRequest {
- private final Set<String> wantRefs;
- private final Set<ObjectId> wantObjects;
- private Map<String, Ref> refs;
-
- /**
- * Construct a new fetch request for a replica.
- *
- * @param wantRefs
- * named references to be fetched.
- * @param wantObjects
- * specific objects to be fetched.
- */
- public ReplicaFetchRequest(Set<String> wantRefs,
- Set<ObjectId> wantObjects) {
- this.wantRefs = wantRefs;
- this.wantObjects = wantObjects;
- }
-
- /**
- * Get references to be fetched.
- *
- * @return references to be fetched.
- */
- public Set<String> getWantRefs() {
- return wantRefs;
- }
-
- /**
- * Get objects to be fetched.
- *
- * @return objects to be fetched.
- */
- public Set<ObjectId> getWantObjects() {
- return wantObjects;
- }
-
- /**
- * Get remote references, usually from the advertisement.
- *
- * @return remote references, usually from the advertisement.
- */
- @Nullable
- public Map<String, Ref> getRefs() {
- return refs;
- }
-
- /**
- * Set references observed from the replica.
- *
- * @param refs
- * references observed from the replica.
- */
- public void setRefs(Map<String, Ref> refs) {
- this.refs = refs;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaPushRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaPushRequest.java
deleted file mode 100644
index 273760bc6e..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaPushRequest.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import java.util.Collection;
-import java.util.Map;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-/**
- * A push request sending objects to a replica, and its result.
- * <p>
- * Implementors of {@link org.eclipse.jgit.internal.ketch.KetchReplica} must
- * populate the command result fields, {@link #setRefs(Map)}, and call one of
- * {@link #setException(Repository, Throwable)} or {@link #done(Repository)} to
- * finish processing.
- */
-public class ReplicaPushRequest {
- private final KetchReplica replica;
- private final Collection<ReceiveCommand> commands;
- private Map<String, Ref> refs;
- private Throwable exception;
- private boolean notified;
-
- /**
- * Construct a new push request for a replica.
- *
- * @param replica
- * the replica being pushed to.
- * @param commands
- * commands to be executed.
- */
- public ReplicaPushRequest(KetchReplica replica,
- Collection<ReceiveCommand> commands) {
- this.replica = replica;
- this.commands = commands;
- }
-
- /**
- * Get commands to be executed, and their results.
- *
- * @return commands to be executed, and their results.
- */
- public Collection<ReceiveCommand> getCommands() {
- return commands;
- }
-
- /**
- * Get remote references, usually from the advertisement.
- *
- * @return remote references, usually from the advertisement.
- */
- @Nullable
- public Map<String, Ref> getRefs() {
- return refs;
- }
-
- /**
- * Set references observed from the replica.
- *
- * @param refs
- * references observed from the replica.
- */
- public void setRefs(Map<String, Ref> refs) {
- this.refs = refs;
- }
-
- /**
- * Get exception thrown, if any.
- *
- * @return exception thrown, if any.
- */
- @Nullable
- public Throwable getException() {
- return exception;
- }
-
- /**
- * Mark the request as crashing with a communication error.
- * <p>
- * This method may take significant time acquiring the leader lock and
- * updating the Ketch state machine with the failure.
- *
- * @param repo
- * local repository reference used by the push attempt.
- * @param err
- * exception thrown during communication.
- */
- public void setException(@Nullable Repository repo, Throwable err) {
- if (KetchReplica.log.isErrorEnabled()) {
- KetchReplica.log.error(describe("failed"), err); //$NON-NLS-1$
- }
- if (!notified) {
- notified = true;
- exception = err;
- replica.afterPush(repo, this);
- }
- }
-
- /**
- * Mark the request as completed without exception.
- * <p>
- * This method may take significant time acquiring the leader lock and
- * updating the Ketch state machine with results from this replica.
- *
- * @param repo
- * local repository reference used by the push attempt.
- */
- public void done(Repository repo) {
- if (KetchReplica.log.isDebugEnabled()) {
- KetchReplica.log.debug(describe("completed")); //$NON-NLS-1$
- }
- if (!notified) {
- notified = true;
- replica.afterPush(repo, this);
- }
- }
-
- private String describe(String heading) {
- StringBuilder b = new StringBuilder();
- b.append("push to "); //$NON-NLS-1$
- b.append(replica.describeForLog());
- b.append(' ').append(heading).append(":\n"); //$NON-NLS-1$
- for (ReceiveCommand cmd : commands) {
- b.append(String.format(
- " %-12s %-12s %s %s", //$NON-NLS-1$
- LeaderSnapshot.str(cmd.getOldId()),
- LeaderSnapshot.str(cmd.getNewId()),
- cmd.getRefName(),
- cmd.getResult()));
- if (cmd.getMessage() != null) {
- b.append(' ').append(cmd.getMessage());
- }
- b.append('\n');
- }
- return b.toString();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaSnapshot.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaSnapshot.java
deleted file mode 100644
index 05e4ed693a..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaSnapshot.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import java.util.Date;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.lib.ObjectId;
-
-/**
- * A snapshot of a replica.
- *
- * @see LeaderSnapshot
- */
-public class ReplicaSnapshot {
- final KetchReplica replica;
- ObjectId accepted;
- ObjectId committed;
- KetchReplica.State state;
- String error;
- long retryAtMillis;
-
- ReplicaSnapshot(KetchReplica replica) {
- this.replica = replica;
- }
-
- /**
- * Get the replica this snapshot describes the state of
- *
- * @return the replica this snapshot describes the state of
- */
- public KetchReplica getReplica() {
- return replica;
- }
-
- /**
- * Get current state of the replica
- *
- * @return current state of the replica
- */
- public KetchReplica.State getState() {
- return state;
- }
-
- /**
- * Get last known Git commit at {@code refs/txn/accepted}
- *
- * @return last known Git commit at {@code refs/txn/accepted}
- */
- @Nullable
- public ObjectId getAccepted() {
- return accepted;
- }
-
- /**
- * Get last known Git commit at {@code refs/txn/committed}
- *
- * @return last known Git commit at {@code refs/txn/committed}
- */
- @Nullable
- public ObjectId getCommitted() {
- return committed;
- }
-
- /**
- * Get error message
- *
- * @return if {@link #getState()} ==
- * {@link org.eclipse.jgit.internal.ketch.KetchReplica.State#OFFLINE}
- * an optional human-readable message from the transport system
- * explaining the failure.
- */
- @Nullable
- public String getErrorMessage() {
- return error;
- }
-
- /**
- * Get when the leader will retry communication with the offline or lagging
- * replica
- *
- * @return time (usually in the future) when the leader will retry
- * communication with the offline or lagging replica; null if no
- * retry is scheduled or necessary.
- */
- @Nullable
- public Date getRetryAt() {
- return retryAtMillis > 0 ? new Date(retryAtMillis) : null;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Round.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Round.java
deleted file mode 100644
index 05da5be056..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Round.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import java.io.IOException;
-import java.util.List;
-
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-/**
- * One round-trip to all replicas proposing a log entry.
- * <p>
- * In Raft a log entry represents a state transition at a specific index in the
- * replicated log. The leader can only append log entries to the log.
- * <p>
- * In Ketch a log entry is recorded under the {@code refs/txn} namespace. This
- * occurs when:
- * <ul>
- * <li>a replica wants to establish itself as a new leader by proposing a new
- * term (see {@link ElectionRound})
- * <li>an established leader wants to gain consensus on new {@link Proposal}s
- * (see {@link ProposalRound})
- * </ul>
- */
-abstract class Round {
- final KetchLeader leader;
- final LogIndex acceptedOldIndex;
- LogIndex acceptedNewIndex;
- List<ReceiveCommand> stageCommands;
-
- Round(KetchLeader leader, LogIndex head) {
- this.leader = leader;
- this.acceptedOldIndex = head;
- }
-
- KetchSystem getSystem() {
- return leader.getSystem();
- }
-
- /**
- * Creates a commit for {@code refs/txn/accepted} and calls
- * {@link #runAsync(AnyObjectId)} to begin execution of the round across
- * the system.
- * <p>
- * If references are being updated (such as in a {@link ProposalRound}) the
- * RefTree may be modified.
- * <p>
- * Invoked without {@link KetchLeader#lock} to build objects.
- *
- * @throws IOException
- * the round cannot build new objects within the leader's
- * repository. The leader may be unable to execute.
- */
- abstract void start() throws IOException;
-
- /**
- * Asynchronously distribute the round's new value for
- * {@code refs/txn/accepted} to all replicas.
- * <p>
- * Invoked by {@link #start()} after new commits have been created for the
- * log. The method passes {@code newId} to {@link KetchLeader} to be
- * distributed to all known replicas.
- *
- * @param newId
- * new value for {@code refs/txn/accepted}.
- */
- void runAsync(AnyObjectId newId) {
- acceptedNewIndex = acceptedOldIndex.nextIndex(newId);
- leader.runAsync(this);
- }
-
- /**
- * Notify the round it was accepted by a majority of the system.
- * <p>
- * Invoked by the leader with {@link KetchLeader#lock} held by the caller.
- */
- abstract void success();
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/StageBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/StageBuilder.java
deleted file mode 100644
index 40d86e1a85..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/StageBuilder.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.treewalk.EmptyTreeIterator;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.treewalk.filter.TreeFilter;
-
-/**
- * Constructs a set of commands to stage content during a proposal.
- */
-public class StageBuilder {
- /**
- * Acceptable number of references to send in a single stage transaction.
- * <p>
- * If the number of unique objects exceeds this amount the builder will
- * attempt to decrease the reference count by chaining commits..
- */
- private static final int SMALL_BATCH_SIZE = 5;
-
- /**
- * Acceptable number of commits to chain together using parent pointers.
- * <p>
- * When staging many unique commits the {@link StageBuilder} batches
- * together unrelated commits as parents of a temporary commit. After the
- * proposal completes the temporary commit is discarded and can be garbage
- * collected by all replicas.
- */
- private static final int TEMP_PARENT_BATCH_SIZE = 128;
-
- private static final byte[] PEEL = { ' ', '^' };
-
- private final String txnStage;
- private final String txnId;
-
- /**
- * Construct a stage builder for a transaction.
- *
- * @param txnStageNamespace
- * namespace for transaction references to build
- * {@code "txnStageNamespace/txnId.n"} style names.
- * @param txnId
- * identifier used to name temporary staging refs.
- */
- public StageBuilder(String txnStageNamespace, ObjectId txnId) {
- this.txnStage = txnStageNamespace;
- this.txnId = txnId.name();
- }
-
- /**
- * Compare two RefTrees and return commands to stage new objects.
- * <p>
- * This method ignores the lineage between the two RefTrees and does a
- * straight diff on the two trees. New objects will be staged. The diff
- * strategy is useful to catch-up a lagging replica, without sending every
- * intermediate step. This may mean the replica does not have the same
- * object set as other replicas if there are rewinds or branch deletes.
- *
- * @param git
- * source repository to read {@code oldTree} and {@code newTree}
- * from.
- * @param oldTree
- * accepted RefTree on the replica ({@code refs/txn/accepted}).
- * Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()} if the
- * remote does not have any ref tree, e.g. a new replica catching
- * up.
- * @param newTree
- * RefTree being sent to the replica. The trees will be compared.
- * @return list of commands to create {@code "refs/txn/stage/..."}
- * references on replicas anchoring new objects into the repository
- * while a transaction gains consensus.
- * @throws java.io.IOException
- * {@code git} cannot be accessed to compare {@code oldTree} and
- * {@code newTree} to build the object set.
- */
- public List<ReceiveCommand> makeStageList(Repository git, ObjectId oldTree,
- ObjectId newTree) throws IOException {
- try (RevWalk rw = new RevWalk(git);
- TreeWalk tw = new TreeWalk(rw.getObjectReader());
- ObjectInserter ins = git.newObjectInserter()) {
- if (AnyObjectId.isEqual(oldTree, ObjectId.zeroId())) {
- tw.addTree(new EmptyTreeIterator());
- } else {
- tw.addTree(rw.parseTree(oldTree));
- }
- tw.addTree(rw.parseTree(newTree));
- tw.setFilter(TreeFilter.ANY_DIFF);
- tw.setRecursive(true);
-
- Set<ObjectId> newObjs = new HashSet<>();
- while (tw.next()) {
- if (tw.getRawMode(1) == TYPE_GITLINK
- && !tw.isPathSuffix(PEEL, 2)) {
- newObjs.add(tw.getObjectId(1));
- }
- }
-
- List<ReceiveCommand> cmds = makeStageList(newObjs, git, ins);
- ins.flush();
- return cmds;
- }
- }
-
- /**
- * Construct a set of commands to stage objects on a replica.
- *
- * @param newObjs
- * objects to send to a replica.
- * @param git
- * local repository to read source objects from. Required to
- * perform minification of {@code newObjs}.
- * @param inserter
- * inserter to write temporary commit objects during minification
- * if many new branches are created by {@code newObjs}.
- * @return list of commands to create {@code "refs/txn/stage/..."}
- * references on replicas anchoring {@code newObjs} into the
- * repository while a transaction gains consensus.
- * @throws java.io.IOException
- * {@code git} cannot be accessed to perform minification of
- * {@code newObjs}.
- */
- public List<ReceiveCommand> makeStageList(Set<ObjectId> newObjs,
- @Nullable Repository git, @Nullable ObjectInserter inserter)
- throws IOException {
- if (git == null || newObjs.size() <= SMALL_BATCH_SIZE) {
- // Without a source repository can only construct unique set.
- List<ReceiveCommand> cmds = new ArrayList<>(newObjs.size());
- for (ObjectId id : newObjs) {
- stage(cmds, id);
- }
- return cmds;
- }
-
- List<ReceiveCommand> cmds = new ArrayList<>();
- List<RevCommit> commits = new ArrayList<>();
- reduceObjects(cmds, commits, git, newObjs);
-
- if (inserter == null || commits.size() <= 1
- || (cmds.size() + commits.size()) <= SMALL_BATCH_SIZE) {
- // Without an inserter to aggregate commits, or for a small set of
- // commits just send one stage ref per commit.
- for (RevCommit c : commits) {
- stage(cmds, c.copy());
- }
- return cmds;
- }
-
- // 'commits' is sorted most recent to least recent commit.
- // Group batches of commits and build a chain.
- // TODO(sop) Cluster by restricted graphs to support filtering.
- ObjectId tip = null;
- for (int end = commits.size(); end > 0;) {
- int start = Math.max(0, end - TEMP_PARENT_BATCH_SIZE);
- List<RevCommit> batch = commits.subList(start, end);
- List<ObjectId> parents = new ArrayList<>(1 + batch.size());
- if (tip != null) {
- parents.add(tip);
- }
- parents.addAll(batch);
-
- CommitBuilder b = new CommitBuilder();
- b.setTreeId(batch.get(0).getTree());
- b.setParentIds(parents);
- b.setAuthor(tmpAuthor(batch));
- b.setCommitter(b.getAuthor());
- tip = inserter.insert(b);
- end = start;
- }
- stage(cmds, tip);
- return cmds;
- }
-
- private static PersonIdent tmpAuthor(List<RevCommit> commits) {
- // Construct a predictable author using most recent commit time.
- int t = 0;
- for (int i = 0; i < commits.size();) {
- t = Math.max(t, commits.get(i).getCommitTime());
- }
- String name = "Ketch Stage"; //$NON-NLS-1$
- String email = "tmp@tmp"; //$NON-NLS-1$
- return new PersonIdent(name, email, t * 1000L, 0);
- }
-
- private void reduceObjects(List<ReceiveCommand> cmds,
- List<RevCommit> commits, Repository git,
- Set<ObjectId> newObjs) throws IOException {
- try (RevWalk rw = new RevWalk(git)) {
- rw.setRetainBody(false);
-
- for (ObjectId id : newObjs) {
- RevObject obj = rw.parseAny(id);
- if (obj instanceof RevCommit) {
- rw.markStart((RevCommit) obj);
- } else {
- stage(cmds, id);
- }
- }
-
- for (RevCommit c; (c = rw.next()) != null;) {
- commits.add(c);
- rw.markUninteresting(c);
- }
- }
- }
-
- private void stage(List<ReceiveCommand> cmds, ObjectId id) {
- int estLen = txnStage.length() + txnId.length() + 5;
- StringBuilder n = new StringBuilder(estLen);
- n.append(txnStage).append(txnId).append('.');
- n.append(Integer.toHexString(cmds.size()));
- cmds.add(new ReceiveCommand(ObjectId.zeroId(), id, n.toString()));
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/TimeIsUncertainException.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/TimeIsUncertainException.java
deleted file mode 100644
index f665e6a438..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/TimeIsUncertainException.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.ketch;
-
-import java.io.IOException;
-
-import org.eclipse.jgit.internal.JGitText;
-
-class TimeIsUncertainException extends IOException {
- private static final long serialVersionUID = 1L;
-
- TimeIsUncertainException() {
- super(JGitText.get().timeIsUncertain);
- }
-
- TimeIsUncertainException(Exception e) {
- super(JGitText.get().timeIsUncertain, e);
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/package-info.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/package-info.java
deleted file mode 100644
index dfe03752ca..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Distributed consensus system built on Git.
- */
-package org.eclipse.jgit.internal.ketch;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedObjectReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/BitmappedObjectReachabilityChecker.java
index 89aef7dc41..d8056490aa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedObjectReachabilityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/BitmappedObjectReachabilityChecker.java
@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-package org.eclipse.jgit.revwalk;
+package org.eclipse.jgit.internal.revwalk;
import java.io.IOException;
import java.util.ArrayList;
@@ -21,12 +21,16 @@ import java.util.stream.Stream;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
+import org.eclipse.jgit.revwalk.BitmapWalker;
+import org.eclipse.jgit.revwalk.ObjectReachabilityChecker;
+import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.RevObject;
/**
* Checks if all objects are reachable from certain starting points using
* bitmaps.
*/
-class BitmappedObjectReachabilityChecker
+public class BitmappedObjectReachabilityChecker
implements ObjectReachabilityChecker {
private final ObjectWalk walk;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/BitmappedReachabilityChecker.java
index 0d9c4593bf..37721ad1ea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/BitmappedReachabilityChecker.java
@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-package org.eclipse.jgit.revwalk;
+package org.eclipse.jgit.internal.revwalk;
import java.io.IOException;
import java.util.ArrayList;
@@ -23,12 +23,17 @@ import org.eclipse.jgit.lib.BitmapIndex;
import org.eclipse.jgit.lib.BitmapIndex.Bitmap;
import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.revwalk.ReachabilityChecker;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevFlag;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.RevFilter;
/**
* Checks the reachability using bitmaps.
*/
-class BitmappedReachabilityChecker implements ReachabilityChecker {
+public class BitmappedReachabilityChecker implements ReachabilityChecker {
private final RevWalk walk;
@@ -42,7 +47,7 @@ class BitmappedReachabilityChecker implements ReachabilityChecker {
* @throws IOException
* if the index or the object reader cannot be opened.
*/
- BitmappedReachabilityChecker(RevWalk walk)
+ public BitmappedReachabilityChecker(RevWalk walk)
throws IOException {
this.walk = walk;
if (walk.getObjectReader().getBitmapIndex() == null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/PedestrianObjectReachabilityChecker.java
index df5d68a66e..1d1f5fddaf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/PedestrianObjectReachabilityChecker.java
@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-package org.eclipse.jgit.revwalk;
+package org.eclipse.jgit.internal.revwalk;
import java.io.IOException;
import java.io.InvalidObjectException;
@@ -17,12 +17,18 @@ import java.util.Optional;
import java.util.stream.Stream;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.revwalk.ObjectReachabilityChecker;
+import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevSort;
/**
* Checks if all objects are reachable from certain starting points doing a
* walk.
*/
-class PedestrianObjectReachabilityChecker implements ObjectReachabilityChecker {
+public class PedestrianObjectReachabilityChecker
+ implements ObjectReachabilityChecker {
private final ObjectWalk walk;
/**
@@ -31,7 +37,7 @@ class PedestrianObjectReachabilityChecker implements ObjectReachabilityChecker {
* @param walk
* ObjectWalk instance to reuse. Caller retains ownership.
*/
- PedestrianObjectReachabilityChecker(ObjectWalk walk) {
+ public PedestrianObjectReachabilityChecker(ObjectWalk walk) {
this.walk = walk;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/PedestrianReachabilityChecker.java
index 5dc03776c2..a03306b6ee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/revwalk/PedestrianReachabilityChecker.java
@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-package org.eclipse.jgit.revwalk;
+package org.eclipse.jgit.internal.revwalk;
import java.io.IOException;
import java.util.Collection;
@@ -17,12 +17,16 @@ import java.util.stream.Stream;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.revwalk.ReachabilityChecker;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.revwalk.RevWalk;
/**
* Checks the reachability walking the graph from the starters towards the
* target.
*/
-class PedestrianReachabilityChecker implements ReachabilityChecker {
+public class PedestrianReachabilityChecker implements ReachabilityChecker {
private final boolean topoSort;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
index 876cbec161..26d5b5b176 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
@@ -13,7 +13,6 @@ package org.eclipse.jgit.internal.storage.dfs;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.COMPACT;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC_REST;
-import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC_TXN;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.INSERT;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.RECEIVE;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
@@ -45,7 +44,6 @@ import org.eclipse.jgit.internal.storage.pack.PackWriter;
import org.eclipse.jgit.internal.storage.reftable.ReftableCompactor;
import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
-import org.eclipse.jgit.internal.storage.reftree.RefTreeNames;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -95,7 +93,6 @@ public class DfsGarbageCollector {
private Set<ObjectId> allHeadsAndTags;
private Set<ObjectId> allTags;
private Set<ObjectId> nonHeads;
- private Set<ObjectId> txnHeads;
private Set<ObjectId> tagTargets;
/**
@@ -318,7 +315,6 @@ public class DfsGarbageCollector {
allHeadsAndTags = new HashSet<>();
allTags = new HashSet<>();
nonHeads = new HashSet<>();
- txnHeads = new HashSet<>();
tagTargets = new HashSet<>();
for (Ref ref : refsBefore) {
if (ref.isSymbolic() || ref.getObjectId() == null) {
@@ -328,8 +324,6 @@ public class DfsGarbageCollector {
allHeads.add(ref.getObjectId());
} else if (isTag(ref)) {
allTags.add(ref.getObjectId());
- } else if (RefTreeNames.isRefTree(refdb, ref.getName())) {
- txnHeads.add(ref.getObjectId());
} else {
nonHeads.add(ref.getObjectId());
}
@@ -355,7 +349,6 @@ public class DfsGarbageCollector {
try {
packHeads(pm);
packRest(pm);
- packRefTreeGraph(pm);
packGarbage(pm);
objdb.commitPack(newPackDesc, toPrune());
rollback = false;
@@ -559,19 +552,6 @@ public class DfsGarbageCollector {
}
}
- private void packRefTreeGraph(ProgressMonitor pm) throws IOException {
- if (txnHeads.isEmpty())
- return;
-
- try (PackWriter pw = newPackWriter()) {
- for (ObjectIdSet packedObjs : newPackObj)
- pw.excludeObjects(packedObjs);
- pw.preparePack(pm, txnHeads, NONE);
- if (0 < pw.getObjectCount())
- writePack(GC_TXN, pw, pm, 0 /* unknown pack size */);
- }
- }
-
private void packGarbage(ProgressMonitor pm) throws IOException {
PackConfig cfg = new PackConfig(packConfig);
cfg.setReuseDeltas(true);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
index 4dab3b20c5..46ec87df54 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
@@ -105,13 +105,6 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
GC_REST,
/**
- * RefTreeGraph pack was created by Git garbage collection.
- *
- * @see DfsGarbageCollector
- */
- GC_TXN,
-
- /**
* Pack was created by Git garbage collection.
* <p>
* This pack contains only unreachable garbage that was found during the
@@ -133,7 +126,6 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
.add(COMPACT)
.add(GC)
.add(GC_REST)
- .add(GC_TXN)
.add(UNREACHABLE_GARBAGE)
.build();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectRepresentation.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectRepresentation.java
index 3f113a3ee3..8e124e3c20 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectRepresentation.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectRepresentation.java
@@ -48,7 +48,6 @@ class DfsObjectRepresentation extends StoredObjectRepresentation {
switch (pack.getPackDescription().getPackSource()) {
case GC:
case GC_REST:
- case GC_TXN:
return true;
default:
return false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
index 0c8755fca3..4f418ab4db 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
@@ -533,7 +533,6 @@ public class DfsPackDescription {
switch (s) {
case GC:
case GC_REST:
- case GC_TXN:
return true;
default:
return false;
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 b1e95520cc..96ca690c1c 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
@@ -607,8 +607,15 @@ public final class DfsPackFile extends BlockBasedFile {
private void readFully(long position, byte[] dstbuf, int dstoff, int cnt,
DfsReader ctx) throws IOException {
- if (ctx.copy(this, position, dstbuf, dstoff, cnt) != cnt)
- throw new EOFException();
+ while (cnt > 0) {
+ int copied = ctx.copy(this, position, dstbuf, dstoff, cnt);
+ if (copied == 0) {
+ throw new EOFException();
+ }
+ position += copied;
+ dstoff += copied;
+ cnt -= copied;
+ }
}
ObjectLoader load(DfsReader ctx, long pos)
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 8a54431d5c..6c3b056efd 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
@@ -17,6 +17,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
+import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.jgit.annotations.Nullable;
@@ -62,11 +63,12 @@ public class DfsReftableDatabase extends DfsRefDatabase {
reftableDatabase = new ReftableDatabase() {
@Override
public MergedReftable openMergedReftable() throws IOException {
- DfsReftableDatabase.this.getLock().lock();
+ Lock l = DfsReftableDatabase.this.getLock();
+ l.lock();
try {
return new MergedReftable(stack().readers());
} finally {
- DfsReftableDatabase.this.getLock().unlock();
+ l.unlock();
}
}
};
@@ -176,6 +178,13 @@ public class DfsReftableDatabase extends DfsRefDatabase {
/** {@inheritDoc} */
@Override
+ public List<Ref> getRefsByPrefixWithExclusions(String include, Set<String> excludes)
+ throws IOException {
+ return reftableDatabase.getRefsByPrefixWithExclusions(include, excludes);
+ }
+
+ /** {@inheritDoc} */
+ @Override
public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
if (!getReftableConfig().isIndexObjects()) {
return super.getTipsWithSha1(id);
@@ -207,7 +216,8 @@ public class DfsReftableDatabase extends DfsRefDatabase {
@Override
void clearCache() {
- getLock().lock();
+ ReentrantLock l = getLock();
+ l.lock();
try {
if (ctx != null) {
ctx.close();
@@ -219,7 +229,7 @@ public class DfsReftableDatabase extends DfsRefDatabase {
stack = null;
}
} finally {
- getLock().unlock();
+ l.unlock();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteArrayWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteArrayWindow.java
index 45d9c85c8c..1036535423 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteArrayWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteArrayWindow.java
@@ -25,7 +25,7 @@ import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
final class ByteArrayWindow extends ByteWindow {
private final byte[] array;
- ByteArrayWindow(PackFile pack, long o, byte[] b) {
+ ByteArrayWindow(Pack pack, long o, byte[] b) {
super(pack, o, b.length);
array = b;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteBufferWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteBufferWindow.java
index 8703216322..b6877578c9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteBufferWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteBufferWindow.java
@@ -27,7 +27,7 @@ import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
final class ByteBufferWindow extends ByteWindow {
private final ByteBuffer buffer;
- ByteBufferWindow(PackFile pack, long o, ByteBuffer b) {
+ ByteBufferWindow(Pack pack, long o, ByteBuffer b) {
super(pack, o, b.capacity());
buffer = b;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteWindow.java
index 159f31c971..31e7eadd8a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteWindow.java
@@ -27,7 +27,7 @@ import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
* </p>
*/
abstract class ByteWindow {
- protected final PackFile pack;
+ protected final Pack pack;
protected final long start;
@@ -37,13 +37,13 @@ abstract class ByteWindow {
* Constructor for ByteWindow.
*
* @param p
- * a {@link org.eclipse.jgit.internal.storage.file.PackFile}.
+ * a {@link org.eclipse.jgit.internal.storage.file.Pack}.
* @param s
* where the byte window starts in the pack file
* @param n
* size of the byte window
*/
- protected ByteWindow(PackFile p, long s, int n) {
+ protected ByteWindow(Pack p, long s, int n) {
pack = p;
start = s;
end = start + n;
@@ -53,8 +53,8 @@ abstract class ByteWindow {
return (int) (end - start);
}
- final boolean contains(PackFile neededFile, long neededPos) {
- return pack == neededFile && start <= neededPos && neededPos < end;
+ final boolean contains(Pack neededPack, long neededPos) {
+ return pack == neededPack && start <= neededPos && neededPos < end;
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
index 9c7a2e7111..7dedeb57ab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
@@ -239,7 +239,7 @@ class CachedObjectDirectory extends FileObjectDatabase {
}
@Override
- PackFile openPack(File pack) throws IOException {
+ Pack openPack(File pack) throws IOException {
return wrapped.openPack(pack);
}
@@ -250,7 +250,7 @@ class CachedObjectDirectory extends FileObjectDatabase {
}
@Override
- Collection<PackFile> getPacks() {
+ Collection<Pack> getPacks() {
return wrapped.getPacks();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java
index cef5a330f2..69cebadf1e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java
@@ -49,7 +49,7 @@ class DeltaBaseCache {
cache = new Slot[CACHE_SZ];
}
- Entry get(PackFile pack, long position) {
+ Entry get(Pack pack, long position) {
Slot e = cache[hash(position)];
if (e == null)
return null;
@@ -63,7 +63,7 @@ class DeltaBaseCache {
return null;
}
- void store(final PackFile pack, final long position,
+ void store(final Pack pack, final long position,
final byte[] data, final int objectType) {
if (data.length > maxByteCount)
return; // Too large to cache.
@@ -146,7 +146,7 @@ class DeltaBaseCache {
Slot lruNext;
- PackFile provider;
+ Pack provider;
long position;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
index 11ed10c90a..01dd27d9fb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
@@ -71,7 +71,7 @@ abstract class FileObjectDatabase extends ObjectDatabase {
abstract InsertLooseObjectResult insertUnpackedObject(File tmp,
ObjectId id, boolean createDuplicate) throws IOException;
- abstract PackFile openPack(File pack) throws IOException;
+ abstract Pack openPack(File pack) throws IOException;
- abstract Collection<PackFile> getPacks();
+ abstract Collection<Pack> getPacks();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
index e613a58062..a80fa837b7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
@@ -21,7 +21,9 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TreeSet;
+import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
@@ -107,12 +109,13 @@ public class FileReftableDatabase extends RefDatabase {
* @throws IOException on I/O errors
*/
public void compactFully() throws IOException {
- reftableDatabase.getLock().lock();
+ Lock l = reftableDatabase.getLock();
+ l.lock();
try {
reftableStack.compactFully();
reftableDatabase.clearCache();
} finally {
- reftableDatabase.getLock().unlock();
+ l.unlock();
}
}
@@ -179,6 +182,13 @@ public class FileReftableDatabase extends RefDatabase {
/** {@inheritDoc} */
@Override
+ public List<Ref> getRefsByPrefixWithExclusions(String include, Set<String> excludes)
+ throws IOException {
+ return reftableDatabase.getRefsByPrefixWithExclusions(include, excludes);
+ }
+
+ /** {@inheritDoc} */
+ @Override
public List<Ref> getAdditionalRefs() throws IOException {
return Collections.emptyList();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java
index bc2039c56b..b5e3927bcc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java
@@ -21,6 +21,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
+import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@@ -65,6 +66,8 @@ public class FileReftableStack implements AutoCloseable {
private final Runnable onChange;
+ private final SecureRandom random = new SecureRandom();
+
private final Supplier<Config> configSupplier;
// Used for stats & testing.
@@ -365,8 +368,9 @@ public class FileReftableStack implements AutoCloseable {
}
private String filename(long low, long high) {
- return String.format("%012x-%012x", //$NON-NLS-1$
- Long.valueOf(low), Long.valueOf(high));
+ return String.format("%012x-%012x-%08x", //$NON-NLS-1$
+ Long.valueOf(low), Long.valueOf(high),
+ Integer.valueOf(random.nextInt()));
}
/**
@@ -636,6 +640,9 @@ public class FileReftableStack implements AutoCloseable {
@Override
public boolean equals(Object other) {
+ if (other == null) {
+ return false;
+ }
Segment o = (Segment) other;
return o.bytes == bytes && o.log == log && o.start == start
&& o.end == end;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
index fd052cec28..fecced1ae6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
@@ -40,7 +40,6 @@ import org.eclipse.jgit.events.IndexChangedEvent;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateHandle;
import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateRepository;
-import org.eclipse.jgit.internal.storage.reftree.RefTreeDatabase;
import org.eclipse.jgit.lib.BaseRepositoryBuilder;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.ConfigConstants;
@@ -182,9 +181,6 @@ public class FileRepository extends Repository {
if (StringUtils.equalsIgnoreCase(reftype,
ConfigConstants.CONFIG_REF_STORAGE_REFTABLE)) {
refs = new FileReftableDatabase(this);
- } else if (StringUtils.equalsIgnoreCase(reftype,
- ConfigConstants.CONFIG_REFSTORAGE_REFTREE)) {
- refs = new RefTreeDatabase(this, new RefDirectory(this));
} else {
throw new IOException(JGitText.get().unknownRepositoryFormat);
}
@@ -247,7 +243,7 @@ public class FileRepository extends Repository {
RefUpdate head = updateRef(Constants.HEAD);
head.disableRefLog();
- head.link(Constants.R_HEADS + Constants.MASTER);
+ head.link(Constants.R_HEADS + getInitialBranch());
final boolean fileMode;
if (getFS().supportsExecute()) {
@@ -640,7 +636,7 @@ public class FileRepository extends Repository {
refsHeadsFile.delete();
// RefDirectory wants to create the refs/ directory from scratch, so
// remove that too.
- refsFile.delete();
+ refsFile.delete();
// remove HEAD so its previous invalid value doesn't cause issues.
headFile.delete();
@@ -668,7 +664,7 @@ public class FileRepository extends Repository {
for (ReflogEntry e : logs) {
logWriter.log(r.getName(), e);
}
- }
+ }
}
try (RevWalk rw = new RevWalk(this)) {
@@ -731,7 +727,7 @@ public class FileRepository extends Repository {
throws IOException {
File reftableDir = new File(getDirectory(), Constants.REFTABLE);
File headFile = new File(getDirectory(), Constants.HEAD);
- if (reftableDir.exists() && reftableDir.listFiles().length > 0) {
+ if (reftableDir.exists() && FileUtils.hasFiles(reftableDir.toPath())) {
throw new IOException(JGitText.get().reftableDirExists);
}
@@ -763,9 +759,11 @@ public class FileRepository extends Repository {
}
} else {
FileUtils.delete(packedRefs, FileUtils.SKIP_MISSING);
- FileUtils.delete(headFile);
- FileUtils.delete(logsDir, FileUtils.RECURSIVE);
- FileUtils.delete(refsFile, FileUtils.RECURSIVE);
+ FileUtils.delete(headFile, FileUtils.SKIP_MISSING);
+ FileUtils.delete(logsDir,
+ FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
+ FileUtils.delete(refsFile,
+ FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
for (String r : additional) {
new File(getDirectory(), r).delete();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
index 54ff7d29c6..8c48cd94b0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
@@ -12,8 +12,10 @@ package org.eclipse.jgit.internal.storage.file;
import static org.eclipse.jgit.util.FS.FileStoreAttributes.FALLBACK_FILESTORE_ATTRIBUTES;
import static org.eclipse.jgit.util.FS.FileStoreAttributes.FALLBACK_TIMESTAMP_RESOLUTION;
+
import java.io.File;
import java.io.IOException;
+import java.nio.file.NoSuchFileException;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Duration;
import java.time.Instant;
@@ -221,14 +223,20 @@ public class FileSnapshot {
this.file = file;
this.lastRead = Instant.now();
this.fileStoreAttributeCache = useConfig
- ? FS.getFileStoreAttributes(file.toPath().getParent())
+ ? FS.getFileStoreAttributes(file.toPath())
: FALLBACK_FILESTORE_ATTRIBUTES;
BasicFileAttributes fileAttributes = null;
try {
fileAttributes = FS.DETECTED.fileAttributes(file);
+ } catch (NoSuchFileException e) {
+ this.lastModified = Instant.EPOCH;
+ this.size = 0L;
+ this.fileKey = MISSING_FILEKEY;
+ return;
} catch (IOException e) {
- this.lastModified = Instant.ofEpochMilli(file.lastModified());
- this.size = file.length();
+ LOG.error(e.getMessage(), e);
+ this.lastModified = Instant.EPOCH;
+ this.size = 0L;
this.fileKey = MISSING_FILEKEY;
return;
}
@@ -309,9 +317,14 @@ public class FileSnapshot {
currLastModified = fileAttributes.lastModifiedTime().toInstant();
currSize = fileAttributes.size();
currFileKey = getFileKey(fileAttributes);
+ } catch (NoSuchFileException e) {
+ currLastModified = Instant.EPOCH;
+ currSize = 0L;
+ currFileKey = MISSING_FILEKEY;
} catch (IOException e) {
- currLastModified = Instant.ofEpochMilli(path.lastModified());
- currSize = path.length();
+ LOG.error(e.getMessage(), e);
+ currLastModified = Instant.EPOCH;
+ currSize = 0L;
currFileKey = MISSING_FILEKEY;
}
sizeChanged = isSizeChanged(currSize);
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 1f2fe1057f..75de3be89e 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
@@ -60,7 +60,6 @@ import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
-import org.eclipse.jgit.internal.storage.reftree.RefTreeNames;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
@@ -206,16 +205,16 @@ public class GC {
* gc.log.
*
* @return the collection of
- * {@link org.eclipse.jgit.internal.storage.file.PackFile}'s which
+ * {@link org.eclipse.jgit.internal.storage.file.Pack}'s which
* are newly created
* @throws java.io.IOException
* @throws java.text.ParseException
* If the configuration parameter "gc.pruneexpire" couldn't be
* parsed
*/
- // TODO(ms): change signature and return Future<Collection<PackFile>>
+ // TODO(ms): change signature and return Future<Collection<Pack>>
@SuppressWarnings("FutureReturnValueIgnored")
- public Collection<PackFile> gc() throws IOException, ParseException {
+ public Collection<Pack> gc() throws IOException, ParseException {
if (!background) {
return doGc();
}
@@ -225,9 +224,9 @@ public class GC {
return Collections.emptyList();
}
- Callable<Collection<PackFile>> gcTask = () -> {
+ Callable<Collection<Pack>> gcTask = () -> {
try {
- Collection<PackFile> newPacks = doGc();
+ Collection<Pack> newPacks = doGc();
if (automatic && tooManyLooseObjects()) {
String message = JGitText.get().gcTooManyUnpruned;
gcLog.write(message);
@@ -259,14 +258,14 @@ public class GC {
return (executor != null) ? executor : WorkQueue.getExecutor();
}
- private Collection<PackFile> doGc() throws IOException, ParseException {
+ private Collection<Pack> doGc() throws IOException, ParseException {
if (automatic && !needGc()) {
return Collections.emptyList();
}
pm.start(6 /* tasks */);
packRefs();
// TODO: implement reflog_expire(pm, repo);
- Collection<PackFile> newPacks = repack();
+ Collection<Pack> newPacks = repack();
prune(Collections.emptySet());
// TODO: implement rerere_gc(pm);
return newPacks;
@@ -282,7 +281,7 @@ public class GC {
* @param existing
* @throws IOException
*/
- private void loosen(ObjectDirectoryInserter inserter, ObjectReader reader, PackFile pack, HashSet<ObjectId> existing)
+ private void loosen(ObjectDirectoryInserter inserter, ObjectReader reader, Pack pack, HashSet<ObjectId> existing)
throws IOException {
for (PackIndex.MutableEntry entry : pack) {
ObjectId oid = entry.toObjectId();
@@ -314,10 +313,10 @@ public class GC {
* @throws ParseException
* @throws IOException
*/
- private void deleteOldPacks(Collection<PackFile> oldPacks,
- Collection<PackFile> newPacks) throws ParseException, IOException {
+ private void deleteOldPacks(Collection<Pack> oldPacks,
+ Collection<Pack> newPacks) throws ParseException, IOException {
HashSet<ObjectId> ids = new HashSet<>();
- for (PackFile pack : newPacks) {
+ for (Pack pack : newPacks) {
for (PackIndex.MutableEntry entry : pack) {
ids.add(entry.toObjectId());
}
@@ -330,12 +329,12 @@ public class GC {
prunePreserved();
long packExpireDate = getPackExpireDate();
- oldPackLoop: for (PackFile oldPack : oldPacks) {
+ oldPackLoop: for (Pack oldPack : oldPacks) {
checkCancelled();
String oldName = oldPack.getPackName();
// check whether an old pack file is also among the list of new
// pack files. Then we must not delete it.
- for (PackFile newPack : newPacks)
+ for (Pack newPack : newPacks)
if (oldName.equals(newPack.getPackName()))
continue oldPackLoop;
@@ -439,7 +438,7 @@ public class GC {
*/
public void prunePacked() throws IOException {
ObjectDirectory objdb = repo.getObjectDatabase();
- Collection<PackFile> packs = objdb.getPacks();
+ Collection<Pack> packs = objdb.getPacks();
File objects = repo.getObjectsDirectory();
String[] fanout = objects.list();
@@ -467,7 +466,7 @@ public class GC {
continue;
}
boolean found = false;
- for (PackFile p : packs) {
+ for (Pack p : packs) {
checkCancelled();
if (p.hasObject(id)) {
found = true;
@@ -789,8 +788,8 @@ public class GC {
* reflog-entries or during writing to the packfiles
* {@link java.io.IOException} occurs
*/
- public Collection<PackFile> repack() throws IOException {
- Collection<PackFile> toBeDeleted = repo.getObjectDatabase().getPacks();
+ public Collection<Pack> repack() throws IOException {
+ Collection<Pack> toBeDeleted = repo.getObjectDatabase().getPacks();
long time = System.currentTimeMillis();
Collection<Ref> refsBefore = getAllRefs();
@@ -802,7 +801,6 @@ public class GC {
Set<ObjectId> txnHeads = new HashSet<>();
Set<ObjectId> tagTargets = new HashSet<>();
Set<ObjectId> indexObjects = listNonHEADIndexObjects();
- RefDatabase refdb = repo.getRefDatabase();
for (Ref ref : refsBefore) {
checkCancelled();
@@ -814,8 +812,6 @@ public class GC {
allHeads.add(ref.getObjectId());
} else if (isTag(ref)) {
allTags.add(ref.getObjectId());
- } else if (RefTreeNames.isRefTree(refdb, ref.getName())) {
- txnHeads.add(ref.getObjectId());
} else {
nonHeads.add(ref.getObjectId());
}
@@ -825,10 +821,10 @@ public class GC {
}
List<ObjectIdSet> excluded = new LinkedList<>();
- for (PackFile f : repo.getObjectDatabase().getPacks()) {
+ for (Pack p : repo.getObjectDatabase().getPacks()) {
checkCancelled();
- if (f.shouldBeKept())
- excluded.add(f.getIndex());
+ if (p.shouldBeKept())
+ excluded.add(p.getIndex());
}
// Don't exclude tags that are also branch tips
@@ -846,8 +842,8 @@ public class GC {
nonHeads.clear();
}
- List<PackFile> ret = new ArrayList<>(2);
- PackFile heads = null;
+ List<Pack> ret = new ArrayList<>(2);
+ Pack heads = null;
if (!allHeadsAndTags.isEmpty()) {
heads = writePack(allHeadsAndTags, PackWriter.NONE, allTags,
tagTargets, excluded);
@@ -857,13 +853,13 @@ public class GC {
}
}
if (!nonHeads.isEmpty()) {
- PackFile rest = writePack(nonHeads, allHeadsAndTags, PackWriter.NONE,
+ Pack rest = writePack(nonHeads, allHeadsAndTags, PackWriter.NONE,
tagTargets, excluded);
if (rest != null)
ret.add(rest);
}
if (!txnHeads.isEmpty()) {
- PackFile txn = writePack(txnHeads, PackWriter.NONE, PackWriter.NONE,
+ Pack txn = writePack(txnHeads, PackWriter.NONE, PackWriter.NONE,
null, excluded);
if (txn != null)
ret.add(txn);
@@ -1133,7 +1129,7 @@ public class GC {
}
}
- private PackFile writePack(@NonNull Set<? extends ObjectId> want,
+ private Pack writePack(@NonNull Set<? extends ObjectId> want,
@NonNull Set<? extends ObjectId> have, @NonNull Set<ObjectId> tags,
Set<ObjectId> tagTargets, List<ObjectIdSet> excludeObjects)
throws IOException {
@@ -1360,13 +1356,13 @@ public class GC {
*/
public RepoStatistics getStatistics() throws IOException {
RepoStatistics ret = new RepoStatistics();
- Collection<PackFile> packs = repo.getObjectDatabase().getPacks();
- for (PackFile f : packs) {
- ret.numberOfPackedObjects += f.getIndex().getObjectCount();
+ Collection<Pack> packs = repo.getObjectDatabase().getPacks();
+ for (Pack p : packs) {
+ ret.numberOfPackedObjects += p.getIndex().getObjectCount();
ret.numberOfPackFiles++;
- ret.sizeOfPackedObjects += f.getPackFile().length();
- if (f.getBitmapIndex() != null)
- ret.numberOfBitmaps += f.getBitmapIndex().getBitmapCount();
+ ret.sizeOfPackedObjects += p.getPackFile().length();
+ if (p.getBitmapIndex() != null)
+ ret.numberOfBitmaps += p.getBitmapIndex().getBitmapCount();
}
File objDir = repo.getObjectsDirectory();
String[] fanout = objDir.list();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LargePackedWholeObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LargePackedWholeObject.java
index ee4bbc1964..e2fbd7a0b4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LargePackedWholeObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LargePackedWholeObject.java
@@ -30,12 +30,12 @@ class LargePackedWholeObject extends ObjectLoader {
private final int headerLength;
- private final PackFile pack;
+ private final Pack pack;
private final FileObjectDatabase db;
LargePackedWholeObject(int type, long size, long objectOffset,
- int headerLength, PackFile pack, FileObjectDatabase db) {
+ int headerLength, Pack pack, FileObjectDatabase db) {
this.type = type;
this.size = size;
this.objectOffset = objectOffset;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalCachedPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalCachedPack.java
index 9d04062e37..ae5bce6985 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalCachedPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalCachedPack.java
@@ -25,31 +25,31 @@ class LocalCachedPack extends CachedPack {
private final String[] packNames;
- private PackFile[] packs;
+ private Pack[] packs;
LocalCachedPack(ObjectDirectory odb, List<String> packNames) {
this.odb = odb;
this.packNames = packNames.toArray(new String[0]);
}
- LocalCachedPack(List<PackFile> packs) {
+ LocalCachedPack(List<Pack> packs) {
odb = null;
packNames = null;
- this.packs = packs.toArray(new PackFile[0]);
+ this.packs = packs.toArray(new Pack[0]);
}
/** {@inheritDoc} */
@Override
public long getObjectCount() throws IOException {
long cnt = 0;
- for (PackFile pack : getPacks())
+ for (Pack pack : getPacks())
cnt += pack.getObjectCount();
return cnt;
}
void copyAsIs(PackOutputStream out, WindowCursor wc)
throws IOException {
- for (PackFile pack : getPacks())
+ for (Pack pack : getPacks())
pack.copyPackAsIs(out, wc);
}
@@ -58,7 +58,7 @@ class LocalCachedPack extends CachedPack {
public boolean hasObject(ObjectToPack obj, StoredObjectRepresentation rep) {
try {
LocalObjectRepresentation local = (LocalObjectRepresentation) rep;
- for (PackFile pack : getPacks()) {
+ for (Pack pack : getPacks()) {
if (local.pack == pack)
return true;
}
@@ -68,9 +68,9 @@ class LocalCachedPack extends CachedPack {
}
}
- private PackFile[] getPacks() throws FileNotFoundException {
+ private Pack[] getPacks() throws FileNotFoundException {
if (packs == null) {
- PackFile[] p = new PackFile[packNames.length];
+ Pack[] p = new Pack[packNames.length];
for (int i = 0; i < packNames.length; i++)
p[i] = getPackFile(packNames[i]);
packs = p;
@@ -78,8 +78,8 @@ class LocalCachedPack extends CachedPack {
return packs;
}
- private PackFile getPackFile(String packName) throws FileNotFoundException {
- for (PackFile pack : odb.getPacks()) {
+ private Pack getPackFile(String packName) throws FileNotFoundException {
+ for (Pack pack : odb.getPacks()) {
if (packName.equals(pack.getPackName()))
return pack;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectRepresentation.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectRepresentation.java
index 3950dde4a5..559718af3a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectRepresentation.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectRepresentation.java
@@ -16,40 +16,40 @@ import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
import org.eclipse.jgit.lib.ObjectId;
class LocalObjectRepresentation extends StoredObjectRepresentation {
- static LocalObjectRepresentation newWhole(PackFile f, long p, long length) {
+ static LocalObjectRepresentation newWhole(Pack pack, long offset, long length) {
LocalObjectRepresentation r = new LocalObjectRepresentation() {
@Override
public int getFormat() {
return PACK_WHOLE;
}
};
- r.pack = f;
- r.offset = p;
+ r.pack = pack;
+ r.offset = offset;
r.length = length;
return r;
}
- static LocalObjectRepresentation newDelta(PackFile f, long p, long n,
+ static LocalObjectRepresentation newDelta(Pack pack, long offset, long length,
ObjectId base) {
LocalObjectRepresentation r = new Delta();
- r.pack = f;
- r.offset = p;
- r.length = n;
+ r.pack = pack;
+ r.offset = offset;
+ r.length = length;
r.baseId = base;
return r;
}
- static LocalObjectRepresentation newDelta(PackFile f, long p, long n,
+ static LocalObjectRepresentation newDelta(Pack pack, long offset, long length,
long base) {
LocalObjectRepresentation r = new Delta();
- r.pack = f;
- r.offset = p;
- r.length = n;
+ r.pack = pack;
+ r.offset = offset;
+ r.length = length;
r.baseOffset = base;
return r;
}
- PackFile pack;
+ Pack pack;
long offset;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectToPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectToPack.java
index 4a0ac1fd84..ac6cd212d5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectToPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectToPack.java
@@ -17,7 +17,7 @@ import org.eclipse.jgit.lib.AnyObjectId;
/** {@link ObjectToPack} for {@link ObjectDirectory}. */
class LocalObjectToPack extends ObjectToPack {
/** Pack to reuse compressed data from, otherwise null. */
- PackFile pack;
+ Pack pack;
/** Offset of the object's header in {@link #pack}. */
long offset;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java
new file mode 100644
index 0000000000..e7cb285c34
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2009, Google Inc. and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.StandardCopyOption;
+import java.util.Set;
+
+import org.eclipse.jgit.internal.storage.file.FileObjectDatabase.InsertLooseObjectResult;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.util.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Traditional file system based loose objects handler.
+ * <p>
+ * This is the loose object representation for a Git object database, where
+ * objects are stored loose by hashing them into directories by their
+ * {@link org.eclipse.jgit.lib.ObjectId}.
+ */
+class LooseObjects {
+ private static final Logger LOG = LoggerFactory
+ .getLogger(LooseObjects.class);
+
+ private final File directory;
+
+ private final UnpackedObjectCache unpackedObjectCache;
+
+ /**
+ * Initialize a reference to an on-disk object directory.
+ *
+ * @param dir
+ * the location of the <code>objects</code> directory.
+ */
+ LooseObjects(File dir) {
+ directory = dir;
+ unpackedObjectCache = new UnpackedObjectCache();
+ }
+
+ /**
+ * Getter for the field <code>directory</code>.
+ *
+ * @return the location of the <code>objects</code> directory.
+ */
+ File getDirectory() {
+ return directory;
+ }
+
+ void create() throws IOException {
+ FileUtils.mkdirs(directory);
+ }
+
+ void close() {
+ unpackedObjectCache.clear();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "LooseObjects[" + directory + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ boolean hasCached(AnyObjectId id) {
+ return unpackedObjectCache.isUnpacked(id);
+ }
+
+ /**
+ * Does the requested object exist as a loose object?
+ *
+ * @param objectId
+ * identity of the object to test for existence of.
+ * @return {@code true} if the specified object is stored as a loose object.
+ */
+ boolean has(AnyObjectId objectId) {
+ return fileFor(objectId).exists();
+ }
+
+ /**
+ * Find objects matching the prefix abbreviation.
+ *
+ * @param matches
+ * set to add any located ObjectIds to. This is an output
+ * parameter.
+ * @param id
+ * prefix to search for.
+ * @param matchLimit
+ * maximum number of results to return. At most this many
+ * ObjectIds should be added to matches before returning.
+ * @return {@code true} if the matches were exhausted before reaching
+ * {@code maxLimit}.
+ */
+ boolean resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+ int matchLimit) {
+ String fanOut = id.name().substring(0, 2);
+ String[] entries = new File(directory, fanOut).list();
+ if (entries != null) {
+ for (String e : entries) {
+ if (e.length() != Constants.OBJECT_ID_STRING_LENGTH - 2) {
+ continue;
+ }
+ try {
+ ObjectId entId = ObjectId.fromString(fanOut + e);
+ if (id.prefixCompare(entId) == 0) {
+ matches.add(entId);
+ }
+ } catch (IllegalArgumentException notId) {
+ continue;
+ }
+ if (matches.size() > matchLimit) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ ObjectLoader open(WindowCursor curs, AnyObjectId id) throws IOException {
+ File path = fileFor(id);
+ try (FileInputStream in = new FileInputStream(path)) {
+ unpackedObjectCache.add(id);
+ return UnpackedObject.open(in, path, id, curs);
+ } catch (FileNotFoundException noFile) {
+ if (path.exists()) {
+ throw noFile;
+ }
+ unpackedObjectCache.remove(id);
+ return null;
+ }
+ }
+
+ long getSize(WindowCursor curs, AnyObjectId id) throws IOException {
+ File f = fileFor(id);
+ try (FileInputStream in = new FileInputStream(f)) {
+ unpackedObjectCache.add(id);
+ return UnpackedObject.getSize(in, id, curs);
+ } catch (FileNotFoundException noFile) {
+ if (f.exists()) {
+ throw noFile;
+ }
+ unpackedObjectCache.remove(id);
+ return -1;
+ }
+ }
+
+ InsertLooseObjectResult insert(File tmp, ObjectId id) throws IOException {
+ final File dst = fileFor(id);
+ if (dst.exists()) {
+ // We want to be extra careful and avoid replacing an object
+ // that already exists. We can't be sure renameTo() would
+ // fail on all platforms if dst exists, so we check first.
+ //
+ FileUtils.delete(tmp, FileUtils.RETRY);
+ return InsertLooseObjectResult.EXISTS_LOOSE;
+ }
+
+ try {
+ return tryMove(tmp, dst, id);
+ } catch (NoSuchFileException e) {
+ // It's possible the directory doesn't exist yet as the object
+ // directories are always lazily created. Note that we try the
+ // rename/move first as the directory likely does exist.
+ //
+ // Create the directory.
+ //
+ FileUtils.mkdir(dst.getParentFile(), true);
+ } catch (IOException e) {
+ // Any other IO error is considered a failure.
+ //
+ LOG.error(e.getMessage(), e);
+ FileUtils.delete(tmp, FileUtils.RETRY);
+ return InsertLooseObjectResult.FAILURE;
+ }
+
+ try {
+ return tryMove(tmp, dst, id);
+ } catch (IOException e) {
+ // The object failed to be renamed into its proper location and
+ // it doesn't exist in the repository either. We really don't
+ // know what went wrong, so fail.
+ //
+ LOG.error(e.getMessage(), e);
+ FileUtils.delete(tmp, FileUtils.RETRY);
+ return InsertLooseObjectResult.FAILURE;
+ }
+ }
+
+ private InsertLooseObjectResult tryMove(File tmp, File dst, ObjectId id)
+ throws IOException {
+ Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst),
+ StandardCopyOption.ATOMIC_MOVE);
+ dst.setReadOnly();
+ unpackedObjectCache.add(id);
+ return InsertLooseObjectResult.INSERTED;
+ }
+
+ /**
+ * Compute the location of a loose object file.
+ *
+ * @param objectId
+ * identity of the object to get the File location for.
+ * @return {@link java.io.File} location of the specified loose object.
+ */
+ File fileFor(AnyObjectId objectId) {
+ String n = objectId.name();
+ String d = n.substring(0, 2);
+ String f = n.substring(2);
+ return new File(new File(getDirectory(), d), f);
+ }
+}
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 d32182864a..e71a960603 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
@@ -16,28 +16,19 @@ import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import java.io.BufferedReader;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.errors.PackInvalidException;
-import org.eclipse.jgit.errors.PackMismatchException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
import org.eclipse.jgit.internal.storage.pack.PackExt;
@@ -45,7 +36,6 @@ import org.eclipse.jgit.internal.storage.pack.PackWriter;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectDatabase;
import org.eclipse.jgit.lib.ObjectId;
@@ -54,8 +44,6 @@ import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Traditional file system based {@link org.eclipse.jgit.lib.ObjectDatabase}.
@@ -63,7 +51,7 @@ import org.slf4j.LoggerFactory;
* This is the classical object database representation for a Git repository,
* where objects are stored loose by hashing them into directories by their
* {@link org.eclipse.jgit.lib.ObjectId}, or are stored in compressed containers
- * known as {@link org.eclipse.jgit.internal.storage.file.PackFile}s.
+ * known as {@link org.eclipse.jgit.internal.storage.file.Pack}s.
* <p>
* Optionally an object database can reference one or more alternates; other
* ObjectDatabase instances that are searched in addition to the current
@@ -76,12 +64,6 @@ import org.slf4j.LoggerFactory;
* considered.
*/
public class ObjectDirectory extends FileObjectDatabase {
- private static final Logger LOG = LoggerFactory
- .getLogger(ObjectDirectory.class);
-
- private static final PackList NO_PACKS = new PackList(
- FileSnapshot.DIRTY, new PackFile[0]);
-
/** Maximum number of candidates offered as resolutions of abbreviation. */
private static final int RESOLVE_ABBREV_LIMIT = 256;
@@ -93,7 +75,9 @@ public class ObjectDirectory extends FileObjectDatabase {
private final File infoDirectory;
- private final File packDirectory;
+ private final LooseObjects loose;
+
+ private final PackDirectory packed;
private final File preservedDirectory;
@@ -103,16 +87,12 @@ public class ObjectDirectory extends FileObjectDatabase {
private final AtomicReference<AlternateHandle[]> alternates;
- private final UnpackedObjectCache unpackedObjectCache;
-
private final File shallowFile;
private FileSnapshot shallowFileSnapshot = FileSnapshot.DIRTY;
private Set<ObjectId> shallowCommitsIds;
- final AtomicReference<PackList> packList;
-
/**
* Initialize a reference to an on-disk object directory.
*
@@ -136,11 +116,11 @@ public class ObjectDirectory extends FileObjectDatabase {
config = cfg;
objects = dir;
infoDirectory = new File(objects, "info"); //$NON-NLS-1$
- packDirectory = new File(objects, "pack"); //$NON-NLS-1$
+ File packDirectory = new File(objects, "pack"); //$NON-NLS-1$
preservedDirectory = new File(packDirectory, "preserved"); //$NON-NLS-1$
alternatesFile = new File(objects, Constants.INFO_ALTERNATES);
- packList = new AtomicReference<>(NO_PACKS);
- unpackedObjectCache = new UnpackedObjectCache();
+ loose = new LooseObjects(objects);
+ packed = new PackDirectory(config, packDirectory);
this.fs = fs;
this.shallowFile = shallowFile;
@@ -158,7 +138,7 @@ public class ObjectDirectory extends FileObjectDatabase {
/** {@inheritDoc} */
@Override
public final File getDirectory() {
- return objects;
+ return loose.getDirectory();
}
/**
@@ -167,7 +147,7 @@ public class ObjectDirectory extends FileObjectDatabase {
* @return the location of the <code>pack</code> directory.
*/
public final File getPackDirectory() {
- return packDirectory;
+ return packed.getDirectory();
}
/**
@@ -188,9 +168,9 @@ public class ObjectDirectory extends FileObjectDatabase {
/** {@inheritDoc} */
@Override
public void create() throws IOException {
- FileUtils.mkdirs(objects);
+ loose.create();
FileUtils.mkdir(infoDirectory);
- FileUtils.mkdir(packDirectory);
+ packed.create();
}
/** {@inheritDoc} */
@@ -212,13 +192,9 @@ public class ObjectDirectory extends FileObjectDatabase {
/** {@inheritDoc} */
@Override
public void close() {
- unpackedObjectCache.clear();
+ loose.close();
- final PackList packs = packList.get();
- if (packs != NO_PACKS && packList.compareAndSet(packs, NO_PACKS)) {
- for (PackFile p : packs.packs)
- p.close();
- }
+ packed.close();
// Fully close all loaded alternates and clear the alternate list.
AlternateHandle[] alt = alternates.get();
@@ -230,12 +206,8 @@ public class ObjectDirectory extends FileObjectDatabase {
/** {@inheritDoc} */
@Override
- public Collection<PackFile> getPacks() {
- PackList list = packList.get();
- if (list == NO_PACKS)
- list = scanPacks(list);
- PackFile[] packs = list.packs;
- return Collections.unmodifiableCollection(Arrays.asList(packs));
+ public Collection<Pack> getPacks() {
+ return packed.getPacks();
}
/**
@@ -244,7 +216,7 @@ public class ObjectDirectory extends FileObjectDatabase {
* Add a single existing pack to the list of available pack files.
*/
@Override
- public PackFile openPack(File pack)
+ public Pack openPack(File pack)
throws IOException {
final String p = pack.getName();
if (p.length() != 50 || !p.startsWith("pack-") || !p.endsWith(".pack")) //$NON-NLS-1$ //$NON-NLS-2$
@@ -263,8 +235,8 @@ public class ObjectDirectory extends FileObjectDatabase {
}
}
- PackFile res = new PackFile(pack, extensions);
- insertPack(res);
+ Pack res = new Pack(pack, extensions);
+ packed.insert(res);
return res;
}
@@ -277,7 +249,7 @@ public class ObjectDirectory extends FileObjectDatabase {
/** {@inheritDoc} */
@Override
public boolean has(AnyObjectId objectId) {
- return unpackedObjectCache.isUnpacked(objectId)
+ return loose.hasCached(objectId)
|| hasPackedInSelfOrAlternate(objectId, null)
|| hasLooseInSelfOrAlternate(objectId, null);
}
@@ -300,7 +272,7 @@ public class ObjectDirectory extends FileObjectDatabase {
private boolean hasLooseInSelfOrAlternate(AnyObjectId objectId,
Set<AlternateHandle.Id> skips) {
- if (fileFor(objectId).exists()) {
+ if (loose.has(objectId)) {
return true;
}
skips = addMe(skips);
@@ -315,25 +287,7 @@ public class ObjectDirectory extends FileObjectDatabase {
}
boolean hasPackedObject(AnyObjectId objectId) {
- PackList pList;
- do {
- pList = packList.get();
- for (PackFile p : pList.packs) {
- try {
- if (p.hasObject(objectId))
- return true;
- } catch (IOException e) {
- // The hasObject call should have only touched the index,
- // so any failure here indicates the index is unreadable
- // by this process, and the pack is likewise not readable.
- LOG.warn(MessageFormat.format(
- JGitText.get().unableToReadPackfile,
- p.getPackFile().getAbsolutePath()), e);
- removePack(p);
- }
- }
- } while (searchPacksAgain(pList));
- return false;
+ return packed.has(objectId);
}
@Override
@@ -345,41 +299,11 @@ public class ObjectDirectory extends FileObjectDatabase {
private void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
Set<AlternateHandle.Id> skips)
throws IOException {
- // Go through the packs once. If we didn't find any resolutions
- // scan for new packs and check once more.
- int oldSize = matches.size();
- PackList pList;
- do {
- pList = packList.get();
- for (PackFile p : pList.packs) {
- try {
- p.resolve(matches, id, RESOLVE_ABBREV_LIMIT);
- p.resetTransientErrorCount();
- } catch (IOException e) {
- handlePackError(e, p);
- }
- if (matches.size() > RESOLVE_ABBREV_LIMIT)
- return;
- }
- } while (matches.size() == oldSize && searchPacksAgain(pList));
-
- String fanOut = id.name().substring(0, 2);
- String[] entries = new File(getDirectory(), fanOut).list();
- if (entries != null) {
- for (String e : entries) {
- if (e.length() != Constants.OBJECT_ID_STRING_LENGTH - 2)
- continue;
- try {
- ObjectId entId = ObjectId.fromString(fanOut + e);
- if (id.prefixCompare(entId) == 0)
- matches.add(entId);
- } catch (IllegalArgumentException notId) {
- continue;
- }
- if (matches.size() > RESOLVE_ABBREV_LIMIT)
- return;
- }
- }
+ if (!packed.resolve(matches, id, RESOLVE_ABBREV_LIMIT))
+ return;
+
+ if (!loose.resolve(matches, id, RESOLVE_ABBREV_LIMIT))
+ return;
skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
@@ -395,7 +319,7 @@ public class ObjectDirectory extends FileObjectDatabase {
@Override
ObjectLoader openObject(WindowCursor curs, AnyObjectId objectId)
throws IOException {
- if (unpackedObjectCache.isUnpacked(objectId)) {
+ if (loose.hasCached(objectId)) {
ObjectLoader ldr = openLooseObject(curs, objectId);
if (ldr != null) {
return ldr;
@@ -446,51 +370,20 @@ public class ObjectDirectory extends FileObjectDatabase {
}
ObjectLoader openPackedObject(WindowCursor curs, AnyObjectId objectId) {
- PackList pList;
- do {
- SEARCH: for (;;) {
- pList = packList.get();
- for (PackFile p : pList.packs) {
- try {
- ObjectLoader ldr = p.get(curs, objectId);
- p.resetTransientErrorCount();
- if (ldr != null)
- return ldr;
- } catch (PackMismatchException e) {
- // Pack was modified; refresh the entire pack list.
- if (searchPacksAgain(pList))
- continue SEARCH;
- } catch (IOException e) {
- handlePackError(e, p);
- }
- }
- break SEARCH;
- }
- } while (searchPacksAgain(pList));
- return null;
+ return packed.open(curs, objectId);
}
@Override
ObjectLoader openLooseObject(WindowCursor curs, AnyObjectId id)
throws IOException {
- File path = fileFor(id);
- try (FileInputStream in = new FileInputStream(path)) {
- unpackedObjectCache.add(id);
- return UnpackedObject.open(in, path, id, curs);
- } catch (FileNotFoundException noFile) {
- if (path.exists()) {
- throw noFile;
- }
- unpackedObjectCache.remove(id);
- return null;
- }
+ return loose.open(curs, id);
}
@Override
long getObjectSize(WindowCursor curs, AnyObjectId id)
throws IOException {
- if (unpackedObjectCache.isUnpacked(id)) {
- long len = getLooseObjectSize(curs, id);
+ if (loose.hasCached(id)) {
+ long len = loose.getSize(curs, id);
if (0 <= len) {
return len;
}
@@ -504,7 +397,7 @@ public class ObjectDirectory extends FileObjectDatabase {
private long getPackedSizeFromSelfOrAlternate(WindowCursor curs,
AnyObjectId id, Set<AlternateHandle.Id> skips) {
- long len = getPackedObjectSize(curs, id);
+ long len = packed.getSize(curs, id);
if (0 <= len) {
return len;
}
@@ -522,7 +415,7 @@ public class ObjectDirectory extends FileObjectDatabase {
private long getLooseSizeFromSelfOrAlternate(WindowCursor curs,
AnyObjectId id, Set<AlternateHandle.Id> skips) throws IOException {
- long len = getLooseObjectSize(curs, id);
+ long len = loose.getSize(curs, id);
if (0 <= len) {
return len;
}
@@ -538,46 +431,6 @@ public class ObjectDirectory extends FileObjectDatabase {
return -1;
}
- private long getPackedObjectSize(WindowCursor curs, AnyObjectId id) {
- PackList pList;
- do {
- SEARCH: for (;;) {
- pList = packList.get();
- for (PackFile p : pList.packs) {
- try {
- long len = p.getObjectSize(curs, id);
- p.resetTransientErrorCount();
- if (0 <= len)
- return len;
- } catch (PackMismatchException e) {
- // Pack was modified; refresh the entire pack list.
- if (searchPacksAgain(pList))
- continue SEARCH;
- } catch (IOException e) {
- handlePackError(e, p);
- }
- }
- break SEARCH;
- }
- } while (searchPacksAgain(pList));
- return -1;
- }
-
- private long getLooseObjectSize(WindowCursor curs, AnyObjectId id)
- throws IOException {
- File f = fileFor(id);
- try (FileInputStream in = new FileInputStream(f)) {
- unpackedObjectCache.add(id);
- return UnpackedObject.getSize(in, id, curs);
- } catch (FileNotFoundException noFile) {
- if (f.exists()) {
- throw noFile;
- }
- unpackedObjectCache.remove(id);
- return -1;
- }
- }
-
@Override
void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
WindowCursor curs) throws IOException {
@@ -586,25 +439,7 @@ public class ObjectDirectory extends FileObjectDatabase {
private void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
WindowCursor curs, Set<AlternateHandle.Id> skips) throws IOException {
- PackList pList = packList.get();
- SEARCH: for (;;) {
- for (PackFile p : pList.packs) {
- try {
- LocalObjectRepresentation rep = p.representation(curs, otp);
- p.resetTransientErrorCount();
- if (rep != null)
- packer.select(otp, rep);
- } catch (PackMismatchException e) {
- // Pack was modified; refresh the entire pack list.
- //
- pList = scanPacks(pList);
- continue SEARCH;
- } catch (IOException e) {
- handlePackError(e, p);
- }
- }
- break SEARCH;
- }
+ packed.selectRepresentation(packer, otp, curs);
skips = addMe(skips);
for (AlternateHandle h : myAlternates()) {
@@ -614,60 +449,12 @@ public class ObjectDirectory extends FileObjectDatabase {
}
}
- private void handlePackError(IOException e, PackFile p) {
- String warnTmpl = null;
- int transientErrorCount = 0;
- String errTmpl = JGitText.get().exceptionWhileReadingPack;
- if ((e instanceof CorruptObjectException)
- || (e instanceof PackInvalidException)) {
- warnTmpl = JGitText.get().corruptPack;
- LOG.warn(MessageFormat.format(warnTmpl,
- p.getPackFile().getAbsolutePath()), e);
- // Assume the pack is corrupted, and remove it from the list.
- removePack(p);
- } else if (e instanceof FileNotFoundException) {
- if (p.getPackFile().exists()) {
- errTmpl = JGitText.get().packInaccessible;
- transientErrorCount = p.incrementTransientErrorCount();
- } else {
- warnTmpl = JGitText.get().packWasDeleted;
- removePack(p);
- }
- } else if (FileUtils.isStaleFileHandleInCausalChain(e)) {
- warnTmpl = JGitText.get().packHandleIsStale;
- removePack(p);
- } else {
- transientErrorCount = p.incrementTransientErrorCount();
- }
- if (warnTmpl != null) {
- LOG.warn(MessageFormat.format(warnTmpl,
- p.getPackFile().getAbsolutePath()), e);
- } else {
- if (doLogExponentialBackoff(transientErrorCount)) {
- // Don't remove the pack from the list, as the error may be
- // transient.
- LOG.error(MessageFormat.format(errTmpl,
- p.getPackFile().getAbsolutePath(),
- Integer.valueOf(transientErrorCount)), e);
- }
- }
- }
-
- /**
- * @param n
- * count of consecutive failures
- * @return @{code true} if i is a power of 2
- */
- private boolean doLogExponentialBackoff(int n) {
- return (n & (n - 1)) == 0;
- }
-
@Override
InsertLooseObjectResult insertUnpackedObject(File tmp, ObjectId id,
boolean createDuplicate) throws IOException {
// If the object is already in the repository, remove temporary file.
//
- if (unpackedObjectCache.isUnpacked(id)) {
+ if (loose.hasCached(id)) {
FileUtils.delete(tmp, FileUtils.RETRY);
return InsertLooseObjectResult.EXISTS_LOOSE;
}
@@ -675,71 +462,7 @@ public class ObjectDirectory extends FileObjectDatabase {
FileUtils.delete(tmp, FileUtils.RETRY);
return InsertLooseObjectResult.EXISTS_PACKED;
}
-
- final File dst = fileFor(id);
- if (dst.exists()) {
- // We want to be extra careful and avoid replacing an object
- // that already exists. We can't be sure renameTo() would
- // fail on all platforms if dst exists, so we check first.
- //
- FileUtils.delete(tmp, FileUtils.RETRY);
- return InsertLooseObjectResult.EXISTS_LOOSE;
- }
-
- try {
- return tryMove(tmp, dst, id);
- } catch (NoSuchFileException e) {
- // It's possible the directory doesn't exist yet as the object
- // directories are always lazily created. Note that we try the
- // rename/move first as the directory likely does exist.
- //
- // Create the directory.
- //
- FileUtils.mkdir(dst.getParentFile(), true);
- } catch (IOException e) {
- // Any other IO error is considered a failure.
- //
- LOG.error(e.getMessage(), e);
- FileUtils.delete(tmp, FileUtils.RETRY);
- return InsertLooseObjectResult.FAILURE;
- }
-
- try {
- return tryMove(tmp, dst, id);
- } catch (IOException e) {
- // The object failed to be renamed into its proper location and
- // it doesn't exist in the repository either. We really don't
- // know what went wrong, so fail.
- //
- LOG.error(e.getMessage(), e);
- FileUtils.delete(tmp, FileUtils.RETRY);
- return InsertLooseObjectResult.FAILURE;
- }
- }
-
- private InsertLooseObjectResult tryMove(File tmp, File dst,
- ObjectId id)
- throws IOException {
- Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst),
- StandardCopyOption.ATOMIC_MOVE);
- dst.setReadOnly();
- unpackedObjectCache.add(id);
- return InsertLooseObjectResult.INSERTED;
- }
-
- boolean searchPacksAgain(PackList old) {
- // Whether to trust the pack folder's modification time. If set
- // to false we will always scan the .git/objects/pack folder to
- // check for new pack files. If set to true (default) we use the
- // lastmodified attribute of the folder and assume that no new
- // pack files can be in this folder if his modification time has
- // not changed.
- boolean trustFolderStat = config.getBoolean(
- ConfigConstants.CONFIG_CORE_SECTION,
- ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
-
- return ((!trustFolderStat) || old.snapshot.isModified(packDirectory))
- && old != scanPacks(old);
+ return loose.insert(tmp, id);
}
@Override
@@ -780,182 +503,13 @@ public class ObjectDirectory extends FileObjectDatabase {
return shallowCommitsIds;
}
- private void insertPack(PackFile pf) {
- PackList o, n;
- do {
- o = packList.get();
-
- // If the pack in question is already present in the list
- // (picked up by a concurrent thread that did a scan?) we
- // do not want to insert it a second time.
- //
- final PackFile[] oldList = o.packs;
- final String name = pf.getPackFile().getName();
- for (PackFile p : oldList) {
- if (name.equals(p.getPackFile().getName()))
- return;
- }
-
- final PackFile[] newList = new PackFile[1 + oldList.length];
- newList[0] = pf;
- System.arraycopy(oldList, 0, newList, 1, oldList.length);
- n = new PackList(o.snapshot, newList);
- } while (!packList.compareAndSet(o, n));
- }
-
- private void removePack(PackFile deadPack) {
- PackList o, n;
- do {
- o = packList.get();
-
- final PackFile[] oldList = o.packs;
- final int j = indexOf(oldList, deadPack);
- if (j < 0)
- break;
-
- final PackFile[] newList = new PackFile[oldList.length - 1];
- System.arraycopy(oldList, 0, newList, 0, j);
- System.arraycopy(oldList, j + 1, newList, j, newList.length - j);
- n = new PackList(o.snapshot, newList);
- } while (!packList.compareAndSet(o, n));
- deadPack.close();
- }
-
- private static int indexOf(PackFile[] list, PackFile pack) {
- for (int i = 0; i < list.length; i++) {
- if (list[i] == pack)
- return i;
- }
- return -1;
- }
-
- private PackList scanPacks(PackList original) {
- synchronized (packList) {
- PackList o, n;
- do {
- o = packList.get();
- if (o != original) {
- // Another thread did the scan for us, while we
- // were blocked on the monitor above.
- //
- return o;
- }
- n = scanPacksImpl(o);
- if (n == o)
- return n;
- } while (!packList.compareAndSet(o, n));
- return n;
- }
- }
-
- private PackList scanPacksImpl(PackList old) {
- final Map<String, PackFile> forReuse = reuseMap(old);
- final FileSnapshot snapshot = FileSnapshot.save(packDirectory);
- final Set<String> names = listPackDirectory();
- final List<PackFile> list = new ArrayList<>(names.size() >> 2);
- boolean foundNew = false;
- for (String indexName : names) {
- // Must match "pack-[0-9a-f]{40}.idx" to be an index.
- //
- if (indexName.length() != 49 || !indexName.endsWith(".idx")) //$NON-NLS-1$
- continue;
-
- final String base = indexName.substring(0, indexName.length() - 3);
- int extensions = 0;
- for (PackExt ext : PackExt.values()) {
- if (names.contains(base + ext.getExtension()))
- extensions |= ext.getBit();
- }
-
- if ((extensions & PACK.getBit()) == 0) {
- // Sometimes C Git's HTTP fetch transport leaves a
- // .idx file behind and does not download the .pack.
- // We have to skip over such useless indexes.
- //
- continue;
- }
-
- final String packName = base + PACK.getExtension();
- final File packFile = new File(packDirectory, packName);
- final PackFile oldPack = forReuse.get(packName);
- if (oldPack != null
- && !oldPack.getFileSnapshot().isModified(packFile)) {
- forReuse.remove(packName);
- list.add(oldPack);
- continue;
- }
-
- list.add(new PackFile(packFile, extensions));
- foundNew = true;
- }
-
- // If we did not discover any new files, the modification time was not
- // changed, and we did not remove any files, then the set of files is
- // the same as the set we were given. Instead of building a new object
- // return the same collection.
- //
- if (!foundNew && forReuse.isEmpty() && snapshot.equals(old.snapshot)) {
- old.snapshot.setClean(snapshot);
- return old;
- }
-
- for (PackFile p : forReuse.values()) {
- p.close();
- }
-
- if (list.isEmpty())
- return new PackList(snapshot, NO_PACKS.packs);
-
- final PackFile[] r = list.toArray(new PackFile[0]);
- Arrays.sort(r, PackFile.SORT);
- return new PackList(snapshot, r);
- }
-
- private static Map<String, PackFile> reuseMap(PackList old) {
- final Map<String, PackFile> forReuse = new HashMap<>();
- for (PackFile p : old.packs) {
- if (p.invalid()) {
- // The pack instance is corrupted, and cannot be safely used
- // again. Do not include it in our reuse map.
- //
- p.close();
- continue;
- }
-
- final PackFile prior = forReuse.put(p.getPackFile().getName(), p);
- if (prior != null) {
- // This should never occur. It should be impossible for us
- // to have two pack files with the same name, as all of them
- // came out of the same directory. If it does, we promised to
- // close any PackFiles we did not reuse, so close the second,
- // readers are likely to be actively using the first.
- //
- forReuse.put(prior.getPackFile().getName(), prior);
- p.close();
- }
- }
- return forReuse;
- }
-
- private Set<String> listPackDirectory() {
- final String[] nameList = packDirectory.list();
- if (nameList == null)
- return Collections.emptySet();
- final Set<String> nameSet = new HashSet<>(nameList.length << 1);
- for (String name : nameList) {
- if (name.startsWith("pack-")) //$NON-NLS-1$
- nameSet.add(name);
- }
- return nameSet;
- }
-
void closeAllPackHandles(File packFile) {
// if the packfile already exists (because we are rewriting a
// packfile for the same set of objects maybe with different
// PackConfig) then make sure we get rid of all handles on the file.
// Windows will not allow for rename otherwise.
if (packFile.exists()) {
- for (PackFile p : getPacks()) {
+ for (Pack p : packed.getPacks()) {
if (packFile.getPath().equals(p.getPackFile().getPath())) {
p.close();
break;
@@ -1025,29 +579,11 @@ public class ObjectDirectory extends FileObjectDatabase {
}
/**
- * {@inheritDoc}
- * <p>
* Compute the location of a loose object file.
*/
@Override
public File fileFor(AnyObjectId objectId) {
- String n = objectId.name();
- String d = n.substring(0, 2);
- String f = n.substring(2);
- return new File(new File(getDirectory(), d), f);
- }
-
- static final class PackList {
- /** State just before reading the pack directory. */
- final FileSnapshot snapshot;
-
- /** All known packs, sorted by {@link PackFile#SORT}. */
- final PackFile[] packs;
-
- PackList(FileSnapshot monitor, PackFile[] packs) {
- this.snapshot = monitor;
- this.packs = packs;
- }
+ return loose.fileFor(objectId);
}
static class AlternateHandle {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
index e27518690b..04d2ff8ab2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
@@ -88,7 +88,7 @@ public class ObjectDirectoryPackParser extends PackParser {
private Deflater def;
/** The pack that was created, if parsing was successful. */
- private PackFile newPack;
+ private Pack newPack;
private PackConfig pconfig;
@@ -129,14 +129,14 @@ public class ObjectDirectoryPackParser extends PackParser {
}
/**
- * Get the imported {@link org.eclipse.jgit.internal.storage.file.PackFile}.
+ * Get the imported {@link org.eclipse.jgit.internal.storage.file.Pack}.
* <p>
* This method is supplied only to support testing; applications shouldn't
* be using it directly to access the imported data.
*
* @return the imported PackFile, if parsing was successful.
*/
- public PackFile getPackFile() {
+ public Pack getPack() {
return newPack;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
index 254c020237..d928633a73 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
@@ -69,13 +69,13 @@ import org.slf4j.LoggerFactory;
* delta packed format yielding high compression of lots of object where some
* objects are similar.
*/
-public class PackFile implements Iterable<PackIndex.MutableEntry> {
- private static final Logger LOG = LoggerFactory.getLogger(PackFile.class);
+public class Pack implements Iterable<PackIndex.MutableEntry> {
+ private static final Logger LOG = LoggerFactory.getLogger(Pack.class);
/**
* Sorts PackFiles to be most recently created to least recently created.
*/
- public static final Comparator<PackFile> SORT = (a, b) -> b.packLastModified
+ public static final Comparator<Pack> SORT = (a, b) -> b.packLastModified
.compareTo(a.packLastModified);
private final File packFile;
@@ -136,7 +136,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
* @param extensions
* additional pack file extensions with the same base as the pack
*/
- public PackFile(File packFile, int extensions) {
+ public Pack(File packFile, int extensions) {
this.packFile = packFile;
this.fileSnapshot = PackFileSnapshot.save(packFile);
this.packLastModified = fileSnapshot.lastModifiedInstant();
@@ -650,6 +650,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
private void doOpen() throws IOException {
if (invalid) {
+ openFail(true, invalidatingCause);
throw new PackInvalidException(packFile, invalidatingCause);
}
try {
@@ -1200,7 +1201,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
@SuppressWarnings("nls")
@Override
public String toString() {
- return "PackFile [packFileName=" + packFile.getName() + ", length="
+ return "Pack [packFileName=" + packFile.getName() + ", length="
+ packFile.length() + ", packChecksum="
+ ObjectId.fromRaw(packChecksum).name() + "]";
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
new file mode 100644
index 0000000000..b2ba36bf91
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2009, Google Inc. and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.PackInvalidException;
+import org.eclipse.jgit.errors.PackMismatchException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.util.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Traditional file system packed objects directory handler.
+ * <p>
+ * This is the {@link org.eclipse.jgit.internal.storage.file.Pack}s object
+ * representation for a Git object database, where objects are stored in
+ * compressed containers known as
+ * {@link org.eclipse.jgit.internal.storage.file.Pack}s.
+ */
+class PackDirectory {
+ private final static Logger LOG = LoggerFactory
+ .getLogger(PackDirectory.class);
+
+ private static final PackList NO_PACKS = new PackList(FileSnapshot.DIRTY,
+ new Pack[0]);
+
+ private final Config config;
+
+ private final File directory;
+
+ private final AtomicReference<PackList> packList;
+
+ /**
+ * Initialize a reference to an on-disk 'pack' directory.
+ *
+ * @param config
+ * configuration this directory consults for write settings.
+ * @param directory
+ * the location of the {@code pack} directory.
+ */
+ PackDirectory(Config config, File directory) {
+ this.config = config;
+ this.directory = directory;
+ packList = new AtomicReference<>(NO_PACKS);
+ }
+
+ /**
+ * Getter for the field {@code directory}.
+ *
+ * @return the location of the {@code pack} directory.
+ */
+ File getDirectory() {
+ return directory;
+ }
+
+ void create() throws IOException {
+ FileUtils.mkdir(directory);
+ }
+
+ void close() {
+ PackList packs = packList.get();
+ if (packs != NO_PACKS && packList.compareAndSet(packs, NO_PACKS)) {
+ for (Pack p : packs.packs) {
+ p.close();
+ }
+ }
+ }
+
+ Collection<Pack> getPacks() {
+ PackList list = packList.get();
+ if (list == NO_PACKS) {
+ list = scanPacks(list);
+ }
+ Pack[] packs = list.packs;
+ return Collections.unmodifiableCollection(Arrays.asList(packs));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "PackDirectory[" + getDirectory() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Does the requested object exist in this PackDirectory?
+ *
+ * @param objectId
+ * identity of the object to test for existence of.
+ * @return true if the specified object is stored in this PackDirectory.
+ */
+ boolean has(AnyObjectId objectId) {
+ PackList pList;
+ do {
+ pList = packList.get();
+ for (Pack p : pList.packs) {
+ try {
+ if (p.hasObject(objectId)) {
+ return true;
+ }
+ } catch (IOException e) {
+ // The hasObject call should have only touched the index,
+ // so any failure here indicates the index is unreadable
+ // by this process, and the pack is likewise not readable.
+ LOG.warn(MessageFormat.format(
+ JGitText.get().unableToReadPackfile,
+ p.getPackFile().getAbsolutePath()), e);
+ remove(p);
+ }
+ }
+ } while (searchPacksAgain(pList));
+ return false;
+ }
+
+ /**
+ * Find objects matching the prefix abbreviation.
+ *
+ * @param matches
+ * set to add any located ObjectIds to. This is an output
+ * parameter.
+ * @param id
+ * prefix to search for.
+ * @param matchLimit
+ * maximum number of results to return. At most this many
+ * ObjectIds should be added to matches before returning.
+ * @return {@code true} if the matches were exhausted before reaching
+ * {@code maxLimit}.
+ */
+ boolean resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+ int matchLimit) {
+ // Go through the packs once. If we didn't find any resolutions
+ // scan for new packs and check once more.
+ int oldSize = matches.size();
+ PackList pList;
+ do {
+ pList = packList.get();
+ for (Pack p : pList.packs) {
+ try {
+ p.resolve(matches, id, matchLimit);
+ p.resetTransientErrorCount();
+ } catch (IOException e) {
+ handlePackError(e, p);
+ }
+ if (matches.size() > matchLimit) {
+ return false;
+ }
+ }
+ } while (matches.size() == oldSize && searchPacksAgain(pList));
+ return true;
+ }
+
+ ObjectLoader open(WindowCursor curs, AnyObjectId objectId) {
+ PackList pList;
+ do {
+ SEARCH: for (;;) {
+ pList = packList.get();
+ for (Pack p : pList.packs) {
+ try {
+ ObjectLoader ldr = p.get(curs, objectId);
+ p.resetTransientErrorCount();
+ if (ldr != null)
+ return ldr;
+ } catch (PackMismatchException e) {
+ // Pack was modified; refresh the entire pack list.
+ if (searchPacksAgain(pList)) {
+ continue SEARCH;
+ }
+ } catch (IOException e) {
+ handlePackError(e, p);
+ }
+ }
+ break SEARCH;
+ }
+ } while (searchPacksAgain(pList));
+ return null;
+ }
+
+ long getSize(WindowCursor curs, AnyObjectId id) {
+ PackList pList;
+ do {
+ SEARCH: for (;;) {
+ pList = packList.get();
+ for (Pack p : pList.packs) {
+ try {
+ long len = p.getObjectSize(curs, id);
+ p.resetTransientErrorCount();
+ if (0 <= len) {
+ return len;
+ }
+ } catch (PackMismatchException e) {
+ // Pack was modified; refresh the entire pack list.
+ if (searchPacksAgain(pList)) {
+ continue SEARCH;
+ }
+ } catch (IOException e) {
+ handlePackError(e, p);
+ }
+ }
+ break SEARCH;
+ }
+ } while (searchPacksAgain(pList));
+ return -1;
+ }
+
+ void selectRepresentation(PackWriter packer, ObjectToPack otp,
+ WindowCursor curs) {
+ PackList pList = packList.get();
+ SEARCH: for (;;) {
+ for (Pack p : pList.packs) {
+ try {
+ LocalObjectRepresentation rep = p.representation(curs, otp);
+ p.resetTransientErrorCount();
+ if (rep != null) {
+ packer.select(otp, rep);
+ }
+ } catch (PackMismatchException e) {
+ // Pack was modified; refresh the entire pack list.
+ //
+ pList = scanPacks(pList);
+ continue SEARCH;
+ } catch (IOException e) {
+ handlePackError(e, p);
+ }
+ }
+ break SEARCH;
+ }
+ }
+
+ private void handlePackError(IOException e, Pack p) {
+ String warnTmpl = null;
+ int transientErrorCount = 0;
+ String errTmpl = JGitText.get().exceptionWhileReadingPack;
+ if ((e instanceof CorruptObjectException)
+ || (e instanceof PackInvalidException)) {
+ warnTmpl = JGitText.get().corruptPack;
+ LOG.warn(MessageFormat.format(warnTmpl,
+ p.getPackFile().getAbsolutePath()), e);
+ // Assume the pack is corrupted, and remove it from the list.
+ remove(p);
+ } else if (e instanceof FileNotFoundException) {
+ if (p.getPackFile().exists()) {
+ errTmpl = JGitText.get().packInaccessible;
+ transientErrorCount = p.incrementTransientErrorCount();
+ } else {
+ warnTmpl = JGitText.get().packWasDeleted;
+ remove(p);
+ }
+ } else if (FileUtils.isStaleFileHandleInCausalChain(e)) {
+ warnTmpl = JGitText.get().packHandleIsStale;
+ remove(p);
+ } else {
+ transientErrorCount = p.incrementTransientErrorCount();
+ }
+ if (warnTmpl != null) {
+ LOG.warn(MessageFormat.format(warnTmpl,
+ p.getPackFile().getAbsolutePath()), e);
+ } else {
+ if (doLogExponentialBackoff(transientErrorCount)) {
+ // Don't remove the pack from the list, as the error may be
+ // transient.
+ LOG.error(MessageFormat.format(errTmpl,
+ p.getPackFile().getAbsolutePath(),
+ Integer.valueOf(transientErrorCount)), e);
+ }
+ }
+ }
+
+ /**
+ * @param n
+ * count of consecutive failures
+ * @return @{code true} if i is a power of 2
+ */
+ private boolean doLogExponentialBackoff(int n) {
+ return (n & (n - 1)) == 0;
+ }
+
+ boolean searchPacksAgain(PackList old) {
+ // Whether to trust the pack folder's modification time. If set
+ // to false we will always scan the .git/objects/pack folder to
+ // check for new pack files. If set to true (default) we use the
+ // lastmodified attribute of the folder and assume that no new
+ // pack files can be in this folder if his modification time has
+ // not changed.
+ boolean trustFolderStat = config.getBoolean(
+ ConfigConstants.CONFIG_CORE_SECTION,
+ ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
+
+ return ((!trustFolderStat) || old.snapshot.isModified(directory))
+ && old != scanPacks(old);
+ }
+
+ void insert(Pack pack) {
+ PackList o, n;
+ do {
+ o = packList.get();
+
+ // If the pack in question is already present in the list
+ // (picked up by a concurrent thread that did a scan?) we
+ // do not want to insert it a second time.
+ //
+ final Pack[] oldList = o.packs;
+ final String name = pack.getPackFile().getName();
+ for (Pack p : oldList) {
+ if (name.equals(p.getPackFile().getName())) {
+ return;
+ }
+ }
+
+ final Pack[] newList = new Pack[1 + oldList.length];
+ newList[0] = pack;
+ System.arraycopy(oldList, 0, newList, 1, oldList.length);
+ n = new PackList(o.snapshot, newList);
+ } while (!packList.compareAndSet(o, n));
+ }
+
+ private void remove(Pack deadPack) {
+ PackList o, n;
+ do {
+ o = packList.get();
+
+ final Pack[] oldList = o.packs;
+ final int j = indexOf(oldList, deadPack);
+ if (j < 0) {
+ break;
+ }
+
+ final Pack[] newList = new Pack[oldList.length - 1];
+ System.arraycopy(oldList, 0, newList, 0, j);
+ System.arraycopy(oldList, j + 1, newList, j, newList.length - j);
+ n = new PackList(o.snapshot, newList);
+ } while (!packList.compareAndSet(o, n));
+ deadPack.close();
+ }
+
+ private static int indexOf(Pack[] list, Pack pack) {
+ for (int i = 0; i < list.length; i++) {
+ if (list[i] == pack) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private PackList scanPacks(PackList original) {
+ synchronized (packList) {
+ PackList o, n;
+ do {
+ o = packList.get();
+ if (o != original) {
+ // Another thread did the scan for us, while we
+ // were blocked on the monitor above.
+ //
+ return o;
+ }
+ n = scanPacksImpl(o);
+ if (n == o) {
+ return n;
+ }
+ } while (!packList.compareAndSet(o, n));
+ return n;
+ }
+ }
+
+ private PackList scanPacksImpl(PackList old) {
+ final Map<String, Pack> forReuse = reuseMap(old);
+ final FileSnapshot snapshot = FileSnapshot.save(directory);
+ final Set<String> names = listPackDirectory();
+ final List<Pack> list = new ArrayList<>(names.size() >> 2);
+ boolean foundNew = false;
+ for (String indexName : names) {
+ // Must match "pack-[0-9a-f]{40}.idx" to be an index.
+ //
+ if (indexName.length() != 49 || !indexName.endsWith(".idx")) { //$NON-NLS-1$
+ continue;
+ }
+
+ final String base = indexName.substring(0, indexName.length() - 3);
+ int extensions = 0;
+ for (PackExt ext : PackExt.values()) {
+ if (names.contains(base + ext.getExtension())) {
+ extensions |= ext.getBit();
+ }
+ }
+
+ if ((extensions & PACK.getBit()) == 0) {
+ // Sometimes C Git's HTTP fetch transport leaves a
+ // .idx file behind and does not download the .pack.
+ // We have to skip over such useless indexes.
+ //
+ continue;
+ }
+
+ final String packName = base + PACK.getExtension();
+ final File packFile = new File(directory, packName);
+ final Pack oldPack = forReuse.get(packName);
+ if (oldPack != null
+ && !oldPack.getFileSnapshot().isModified(packFile)) {
+ forReuse.remove(packName);
+ list.add(oldPack);
+ continue;
+ }
+
+ list.add(new Pack(packFile, extensions));
+ foundNew = true;
+ }
+
+ // If we did not discover any new files, the modification time was not
+ // changed, and we did not remove any files, then the set of files is
+ // the same as the set we were given. Instead of building a new object
+ // return the same collection.
+ //
+ if (!foundNew && forReuse.isEmpty() && snapshot.equals(old.snapshot)) {
+ old.snapshot.setClean(snapshot);
+ return old;
+ }
+
+ for (Pack p : forReuse.values()) {
+ p.close();
+ }
+
+ if (list.isEmpty()) {
+ return new PackList(snapshot, NO_PACKS.packs);
+ }
+
+ final Pack[] r = list.toArray(new Pack[0]);
+ Arrays.sort(r, Pack.SORT);
+ return new PackList(snapshot, r);
+ }
+
+ private static Map<String, Pack> reuseMap(PackList old) {
+ final Map<String, Pack> forReuse = new HashMap<>();
+ for (Pack p : old.packs) {
+ if (p.invalid()) {
+ // The pack instance is corrupted, and cannot be safely used
+ // again. Do not include it in our reuse map.
+ //
+ p.close();
+ continue;
+ }
+
+ final Pack prior = forReuse.put(p.getPackFile().getName(), p);
+ if (prior != null) {
+ // This should never occur. It should be impossible for us
+ // to have two pack files with the same name, as all of them
+ // came out of the same directory. If it does, we promised to
+ // close any PackFiles we did not reuse, so close the second,
+ // readers are likely to be actively using the first.
+ //
+ forReuse.put(prior.getPackFile().getName(), prior);
+ p.close();
+ }
+ }
+ return forReuse;
+ }
+
+ private Set<String> listPackDirectory() {
+ final String[] nameList = directory.list();
+ if (nameList == null) {
+ return Collections.emptySet();
+ }
+ final Set<String> nameSet = new HashSet<>(nameList.length << 1);
+ for (String name : nameList) {
+ if (name.startsWith("pack-")) { //$NON-NLS-1$
+ nameSet.add(name);
+ }
+ }
+ return nameSet;
+ }
+
+ static final class PackList {
+ /** State just before reading the pack directory. */
+ final FileSnapshot snapshot;
+
+ /** All known packs, sorted by {@link Pack#SORT}. */
+ final Pack[] packs;
+
+ PackList(FileSnapshot monitor, Pack[] packs) {
+ this.snapshot = monitor;
+ this.packs = packs;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
index 31686befc9..942cc96745 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
@@ -34,7 +34,7 @@ import org.eclipse.jgit.util.io.SilentFileInputStream;
/**
* Access path to locate objects by {@link org.eclipse.jgit.lib.ObjectId} in a
- * {@link org.eclipse.jgit.internal.storage.file.PackFile}.
+ * {@link org.eclipse.jgit.internal.storage.file.Pack}.
* <p>
* Indexes are strictly redundant information in that we can rebuild all of the
* data held in the index file from the on disk representation of the pack file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java
index 612b12366c..87e0b44d46 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java
@@ -25,7 +25,7 @@ import org.eclipse.jgit.util.NB;
/**
* Creates a table of contents to support random access by
- * {@link org.eclipse.jgit.internal.storage.file.PackFile}.
+ * {@link org.eclipse.jgit.internal.storage.file.Pack}.
* <p>
* Pack index files (the <code>.idx</code> suffix in a pack file pair) provides
* random access to any object in the pack by associating an ObjectId to the
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
index a9e0588885..0bceca72ea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
@@ -16,11 +16,11 @@ import java.io.InputStream;
class PackInputStream extends InputStream {
private final WindowCursor wc;
- private final PackFile pack;
+ private final Pack pack;
private long pos;
- PackInputStream(PackFile pack, long pos, WindowCursor wc)
+ PackInputStream(Pack pack, long pos, WindowCursor wc)
throws IOException {
this.pack = pack;
this.pos = pos;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackLock.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackLock.java
index 2c2f7911aa..482b143e33 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackLock.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackLock.java
@@ -18,7 +18,7 @@ import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
/**
- * Keeps track of a {@link org.eclipse.jgit.internal.storage.file.PackFile}'s
+ * Keeps track of a {@link org.eclipse.jgit.internal.storage.file.Pack}'s
* associated <code>.keep</code> file.
*/
public class PackLock {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java
index 4d80a0312a..ee458e27ba 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java
@@ -25,7 +25,7 @@ import org.eclipse.jgit.lib.ObjectId;
* </p>
*
* @see PackIndex
- * @see PackFile
+ * @see Pack
*/
public class PackReverseIndex {
/** Index we were created from, and that has our ObjectId data. */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java
index 9dbdbc73f0..2c0ade681b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java
@@ -51,9 +51,6 @@ class RefDirectoryRename extends RefRename {
*/
private ObjectId objId;
- /** True if HEAD must be moved to the destination reference. */
- private boolean updateHEAD;
-
/** A reference we backup {@link #objId} into during the rename. */
private RefDirectoryUpdate tmp;
@@ -69,7 +66,7 @@ class RefDirectoryRename extends RefRename {
return Result.IO_FAILURE; // not supported
objId = source.getOldObjectId();
- updateHEAD = needToUpdateHEAD();
+ boolean updateHEAD = needToUpdateHEAD();
tmp = refdb.newTemporaryUpdate();
try (RevWalk rw = new RevWalk(refdb.getRepository())) {
// First backup the source so its never unreachable.
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 3e8cb3a3f2..25653b3ce3 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
@@ -33,7 +33,7 @@ import org.eclipse.jgit.storage.file.WindowCacheStats;
import org.eclipse.jgit.util.Monitoring;
/**
- * Caches slices of a {@link org.eclipse.jgit.internal.storage.file.PackFile} in
+ * Caches slices of a {@link org.eclipse.jgit.internal.storage.file.Pack} in
* memory for faster read access.
* <p>
* The WindowCache serves as a Java based "buffer cache", loading segments of a
@@ -41,7 +41,7 @@ import org.eclipse.jgit.util.Monitoring;
* only tiny slices of a file, the WindowCache tries to smooth out these tiny
* reads into larger block-sized IO operations.
* <p>
- * Whenever a cache miss occurs, {@link #load(PackFile, long)} is invoked by
+ * Whenever a cache miss occurs, {@link #load(Pack, long)} is invoked by
* exactly one thread for the given <code>(PackFile,position)</code> key tuple.
* This is ensured by an array of locks, with the tuple hashed to a lock
* instance.
@@ -80,10 +80,10 @@ import org.eclipse.jgit.util.Monitoring;
* <p>
* This cache has an implementation rule such that:
* <ul>
- * <li>{@link #load(PackFile, long)} is invoked by at most one thread at a time
+ * <li>{@link #load(Pack, long)} is invoked by at most one thread at a time
* for a given <code>(PackFile,position)</code> tuple.</li>
* <li>For every <code>load()</code> invocation there is exactly one
- * {@link #createRef(PackFile, long, ByteWindow)} invocation to wrap a
+ * {@link #createRef(Pack, long, ByteWindow)} invocation to wrap a
* SoftReference or a StrongReference around the cached entity.</li>
* <li>For every Reference created by <code>createRef()</code> there will be
* exactly one call to {@link #clear(PageRef)} to cleanup any resources associated
@@ -91,10 +91,10 @@ import org.eclipse.jgit.util.Monitoring;
* </ul>
* <p>
* Therefore, it is safe to perform resource accounting increments during the
- * {@link #load(PackFile, long)} or
- * {@link #createRef(PackFile, long, ByteWindow)} methods, and matching
+ * {@link #load(Pack, long)} or
+ * {@link #createRef(Pack, long, ByteWindow)} methods, and matching
* decrements during {@link #clear(PageRef)}. Implementors may need to override
- * {@link #createRef(PackFile, long, ByteWindow)} in order to embed additional
+ * {@link #createRef(Pack, long, ByteWindow)} in order to embed additional
* accounting information into an implementation specific
* {@link org.eclipse.jgit.internal.storage.file.WindowCache.PageRef} subclass, as
* the cached entity may have already been evicted by the JRE's garbage
@@ -170,7 +170,7 @@ public class WindowCache {
* @param delta
* delta of cached bytes
*/
- void recordOpenBytes(PackFile pack, int delta);
+ void recordOpenBytes(Pack pack, int delta);
/**
* Returns a snapshot of this recorder's stats. Note that this may be an
@@ -242,7 +242,7 @@ public class WindowCache {
}
@Override
- public void recordOpenBytes(PackFile pack, int delta) {
+ public void recordOpenBytes(Pack pack, int delta) {
openByteCount.add(delta);
String repositoryId = repositoryId(pack);
LongAdder la = openByteCountPerRepository
@@ -254,9 +254,8 @@ public class WindowCache {
}
}
- private static String repositoryId(PackFile pack) {
- // use repository's gitdir since packfile doesn't know its
- // repository
+ private static String repositoryId(Pack pack) {
+ // use repository's gitdir since Pack doesn't know its repository
return pack.getPackFile().getParentFile().getParentFile()
.getParent();
}
@@ -380,7 +379,7 @@ public class WindowCache {
return cache.publishMBeanIfNeeded();
}
- static final ByteWindow get(PackFile pack, long offset)
+ static final ByteWindow get(Pack pack, long offset)
throws IOException {
final WindowCache c = cache;
final ByteWindow r = c.getOrLoad(pack, c.toStart(offset));
@@ -395,7 +394,7 @@ public class WindowCache {
return r;
}
- static final void purge(PackFile pack) {
+ static final void purge(Pack pack) {
cache.removeAll(pack);
}
@@ -506,7 +505,7 @@ public class WindowCache {
return packHash + (int) (off >>> windowSizeShift);
}
- private ByteWindow load(PackFile pack, long offset) throws IOException {
+ private ByteWindow load(Pack pack, long offset) throws IOException {
long startTime = System.nanoTime();
if (pack.beginWindowCache())
statsRecorder.recordOpenFiles(1);
@@ -525,7 +524,7 @@ public class WindowCache {
}
}
- private PageRef<ByteWindow> createRef(PackFile p, long o, ByteWindow v) {
+ private PageRef<ByteWindow> createRef(Pack p, long o, ByteWindow v) {
final PageRef<ByteWindow> ref = useStrongRefs
? new StrongRef(p, o, v, queue)
: new SoftRef(p, o, v, (SoftCleanupQueue) queue);
@@ -539,7 +538,7 @@ public class WindowCache {
close(ref.getPack());
}
- private void close(PackFile pack) {
+ private void close(Pack pack) {
if (pack.endWindowCache()) {
statsRecorder.recordOpenFiles(-1);
}
@@ -578,9 +577,9 @@ public class WindowCache {
* @return the object reference.
* @throws IOException
* the object reference was not in the cache and could not be
- * obtained by {@link #load(PackFile, long)}.
+ * obtained by {@link #load(Pack, long)}.
*/
- private ByteWindow getOrLoad(PackFile pack, long position)
+ private ByteWindow getOrLoad(Pack pack, long position)
throws IOException {
final int slot = slot(pack, position);
final Entry e1 = table.get(slot);
@@ -623,7 +622,7 @@ public class WindowCache {
return v;
}
- private ByteWindow scan(Entry n, PackFile pack, long position) {
+ private ByteWindow scan(Entry n, Pack pack, long position) {
for (; n != null; n = n.next) {
final PageRef<ByteWindow> r = n.ref;
if (r.getPack() == pack && r.getPosition() == position) {
@@ -704,7 +703,7 @@ public class WindowCache {
/**
* Clear all entries related to a single file.
* <p>
- * Typically this method is invoked during {@link PackFile#close()}, when we
+ * Typically this method is invoked during {@link Pack#close()}, when we
* know the pack is never going to be useful to us again (for example, it no
* longer exists on disk). A concurrent reader loading an entry from this
* same pack may cause the pack to become stuck in the cache anyway.
@@ -712,7 +711,7 @@ public class WindowCache {
* @param pack
* the file to purge all entries of.
*/
- private void removeAll(PackFile pack) {
+ private void removeAll(Pack pack) {
for (int s = 0; s < tableSize; s++) {
final Entry e1 = table.get(s);
boolean hasDead = false;
@@ -733,11 +732,11 @@ public class WindowCache {
queue.gc();
}
- private int slot(PackFile pack, long position) {
+ private int slot(Pack pack, long position) {
return (hash(pack.hash, position) >>> 1) % tableSize;
}
- private Lock lock(PackFile pack, long position) {
+ private Lock lock(Pack pack, long position) {
return locks[(hash(pack.hash, position) >>> 1) % locks.length];
}
@@ -799,16 +798,20 @@ public class WindowCache {
boolean kill();
/**
- * Get the packfile the referenced cache page is allocated for
+ * Get the {@link org.eclipse.jgit.internal.storage.file.Pack} the
+ * referenced cache page is allocated for
*
- * @return the packfile the referenced cache page is allocated for
+ * @return the {@link org.eclipse.jgit.internal.storage.file.Pack} the
+ * referenced cache page is allocated for
*/
- PackFile getPack();
+ Pack getPack();
/**
- * Get the position of the referenced cache page in the packfile
+ * Get the position of the referenced cache page in the
+ * {@link org.eclipse.jgit.internal.storage.file.Pack}
*
- * @return the position of the referenced cache page in the packfile
+ * @return the position of the referenced cache page in the
+ * {@link org.eclipse.jgit.internal.storage.file.Pack}
*/
long getPosition();
@@ -844,7 +847,7 @@ public class WindowCache {
/** A soft reference wrapped around a cached object. */
private static class SoftRef extends SoftReference<ByteWindow>
implements PageRef<ByteWindow> {
- private final PackFile pack;
+ private final Pack pack;
private final long position;
@@ -852,7 +855,7 @@ public class WindowCache {
private long lastAccess;
- protected SoftRef(final PackFile pack, final long position,
+ protected SoftRef(final Pack pack, final long position,
final ByteWindow v, final SoftCleanupQueue queue) {
super(v, queue);
this.pack = pack;
@@ -861,7 +864,7 @@ public class WindowCache {
}
@Override
- public PackFile getPack() {
+ public Pack getPack() {
return pack;
}
@@ -900,7 +903,7 @@ public class WindowCache {
private static class StrongRef implements PageRef<ByteWindow> {
private ByteWindow referent;
- private final PackFile pack;
+ private final Pack pack;
private final long position;
@@ -910,7 +913,7 @@ public class WindowCache {
private CleanupQueue queue;
- protected StrongRef(final PackFile pack, final long position,
+ protected StrongRef(final Pack pack, final long position,
final ByteWindow v, final CleanupQueue queue) {
this.pack = pack;
this.position = position;
@@ -920,7 +923,7 @@ public class WindowCache {
}
@Override
- public PackFile getPack() {
+ public Pack getPack() {
return pack;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
index 6c975708a3..e7fd7b9e76 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
@@ -86,7 +86,7 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
/** {@inheritDoc} */
@Override
public BitmapIndex getBitmapIndex() throws IOException {
- for (PackFile pack : db.getPacks()) {
+ for (Pack pack : db.getPacks()) {
PackBitmapIndex index = pack.getBitmapIndex();
if (index != null)
return new BitmapIndexImpl(index);
@@ -98,7 +98,7 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
@Override
public Collection<CachedPack> getCachedPacksAndUpdate(
BitmapBuilder needBitmap) throws IOException {
- for (PackFile pack : db.getPacks()) {
+ for (Pack pack : db.getPacks()) {
PackBitmapIndex index = pack.getBitmapIndex();
if (needBitmap.removeAllOrNone(index))
return Collections.<CachedPack> singletonList(
@@ -218,7 +218,7 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
* this cursor does not match the provider or id and the proper
* window could not be acquired through the provider's cache.
*/
- int copy(final PackFile pack, long position, final byte[] dstbuf,
+ int copy(final Pack pack, long position, final byte[] dstbuf,
int dstoff, final int cnt) throws IOException {
final long length = pack.length;
int need = cnt;
@@ -239,7 +239,7 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
((LocalCachedPack) pack).copyAsIs(out, this);
}
- void copyPackAsIs(final PackFile pack, final long length,
+ void copyPackAsIs(final Pack pack, final long length,
final PackOutputStream out) throws IOException {
long position = 12;
long remaining = length - (12 + 20);
@@ -275,7 +275,7 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
* the inflater encountered an invalid chunk of data. Data
* stream corruption is likely.
*/
- int inflate(final PackFile pack, long position, final byte[] dstbuf,
+ int inflate(final Pack pack, long position, final byte[] dstbuf,
boolean headerOnly) throws IOException, DataFormatException {
prepareInflater();
pin(pack, position);
@@ -293,7 +293,7 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
}
}
- ByteArrayWindow quickCopy(PackFile p, long pos, long cnt)
+ ByteArrayWindow quickCopy(Pack p, long pos, long cnt)
throws IOException {
pin(p, pos);
if (window instanceof ByteArrayWindow
@@ -314,7 +314,7 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
inf.reset();
}
- void pin(PackFile pack, long position)
+ void pin(Pack pack, long position)
throws IOException {
final ByteWindow w = window;
if (w == null || !w.contains(pack, position)) {
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 a78f4d24da..e210acf058 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
@@ -11,6 +11,7 @@
package org.eclipse.jgit.internal.storage.reftable;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
@@ -215,6 +216,23 @@ public class MergedReftable extends Reftable {
}
}
+ @Override
+ public void seekPastPrefix(String prefixName) throws IOException {
+ List<RefQueueEntry> entriesToAdd = new ArrayList<>();
+ entriesToAdd.addAll(queue);
+ if (head != null) {
+ entriesToAdd.add(head);
+ }
+
+ head = null;
+ queue.clear();
+
+ for(RefQueueEntry entry : entriesToAdd){
+ entry.rc.seekPastPrefix(prefixName);
+ add(entry);
+ }
+ }
+
private RefQueueEntry poll() {
RefQueueEntry e = head;
if (e != null) {
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 d96648eb50..5e2c350883 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
@@ -29,6 +29,19 @@ public abstract class RefCursor implements AutoCloseable {
public abstract boolean next() throws IOException;
/**
+ * Seeks forward to the first ref record lexicographically beyond
+ * {@code prefixName} that doesn't start with {@code prefixName}. If there are
+ * no more results, skipping some refs won't add new results. E.g if we create a
+ * RefCursor that returns only results with a specific prefix, skipping that
+ * prefix won't give results that are not part of the original prefix.
+ *
+ * @param prefixName prefix that should be skipped. All previous refs before it
+ * will be skipped.
+ * @throws java.io.IOException references cannot be read.
+ */
+ public abstract void seekPastPrefix(String prefixName) throws IOException;
+
+ /**
* Get reference at the current position.
*
* @return reference at the current position.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java
index 4747be3544..0c16828617 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java
@@ -14,10 +14,12 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.lib.ObjectId;
@@ -266,6 +268,54 @@ public abstract class ReftableDatabase {
}
/**
+ * Returns refs whose names start with a given prefix excluding all refs that
+ * start with one of the given prefixes.
+ *
+ * @param include string that names of refs should start with; may be empty.
+ * @param excludes strings that names of refs can't start with; may be empty.
+ * @return immutable list of refs whose names start with {@code include} and
+ * none of the strings in {@code exclude}.
+ * @throws java.io.IOException the reference space cannot be accessed.
+ */
+ public List<Ref> getRefsByPrefixWithExclusions(String include, Set<String> excludes) throws IOException {
+ if (excludes.isEmpty()) {
+ return getRefsByPrefix(include);
+ }
+ List<Ref> results = new ArrayList<>();
+ lock.lock();
+ try {
+ Reftable table = reader();
+ Iterator<String> excludeIterator =
+ excludes.stream().sorted().collect(Collectors.toList()).iterator();
+ String currentExclusion = excludeIterator.hasNext() ? excludeIterator.next() : null;
+ try (RefCursor rc = RefDatabase.ALL.equals(include) ? table.allRefs() : table.seekRefsWithPrefix(include)) {
+ while (rc.next()) {
+ Ref ref = table.resolve(rc.getRef());
+ if (ref == null || ref.getObjectId() == null) {
+ continue;
+ }
+ // Skip prefixes that will never see since we are already further than those
+ // prefixes lexicographically.
+ while (excludeIterator.hasNext() && !ref.getName().startsWith(currentExclusion)
+ && ref.getName().compareTo(currentExclusion) > 0) {
+ currentExclusion = excludeIterator.next();
+ }
+
+ if (currentExclusion != null && ref.getName().startsWith(currentExclusion)) {
+ rc.seekPastPrefix(currentExclusion);
+ continue;
+ }
+ results.add(ref);
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+
+ return Collections.unmodifiableList(results);
+ }
+
+ /**
* @return whether there is a fast SHA1 to ref map.
* @throws IOException in case of I/O problems.
*/
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 095276f57b..9e2ae91608 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
@@ -509,6 +509,21 @@ public class ReftableReader extends Reftable implements AutoCloseable {
}
@Override
+ public void seekPastPrefix(String prefixName) throws IOException {
+ initRefIndex();
+ byte[] key = prefixName.getBytes(UTF_8);
+ ByteBuffer byteBuffer = ByteBuffer.allocate(key.length + 1);
+ byteBuffer.put(key);
+ // Add the representation of the last byte lexicographically. Based on how UTF_8
+ // representation works, this byte will be bigger lexicographically than any
+ // UTF_8 character when translated into bytes, since 0xFF can never be a part of
+ // a UTF_8 string.
+ byteBuffer.put((byte) 0xFF);
+
+ block = seek(REF_BLOCK_TYPE, byteBuffer.array(), refIndex, 0, refEnd);
+ }
+
+ @Override
public Ref getRef() {
return ref;
}
@@ -682,6 +697,17 @@ public class ReftableReader extends Reftable implements AutoCloseable {
}
@Override
+ /**
+ * The implementation here would not be efficient complexity-wise since it
+ * expected that there are a small number of refs that match the same object id.
+ * In such case it's better to not even use this method (as the caller might
+ * expect it to be efficient).
+ */
+ public void seekPastPrefix(String prefixName) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public Ref getRef() {
return ref;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/AlwaysFailUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/AlwaysFailUpdate.java
deleted file mode 100644
index 5138636089..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/AlwaysFailUpdate.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.storage.reftree;
-
-import java.io.IOException;
-
-import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDatabase;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-
-/** Update that always rejects with {@code LOCK_FAILURE}. */
-class AlwaysFailUpdate extends RefUpdate {
- private final RefTreeDatabase refdb;
-
- AlwaysFailUpdate(RefTreeDatabase refdb, String name) {
- super(new ObjectIdRef.Unpeeled(Ref.Storage.NEW, name, null));
- this.refdb = refdb;
- setCheckConflicting(false);
- }
-
- /** {@inheritDoc} */
- @Override
- protected RefDatabase getRefDatabase() {
- return refdb;
- }
-
- /** {@inheritDoc} */
- @Override
- protected Repository getRepository() {
- return refdb.getRepository();
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean tryLock(boolean deref) throws IOException {
- return false;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void unlock() {
- // No locks are held here.
- }
-
- /** {@inheritDoc} */
- @Override
- protected Result doUpdate(Result desiredResult) {
- return Result.LOCK_FAILURE;
- }
-
- /** {@inheritDoc} */
- @Override
- protected Result doDelete(Result desiredResult) {
- return Result.LOCK_FAILURE;
- }
-
- /** {@inheritDoc} */
- @Override
- protected Result doLink(String target) {
- return Result.LOCK_FAILURE;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Command.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Command.java
deleted file mode 100644
index bb06a9e0cf..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Command.java
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.storage.reftree;
-
-import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
-import static org.eclipse.jgit.lib.Constants.encode;
-import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
-import static org.eclipse.jgit.lib.FileMode.TYPE_SYMLINK;
-import static org.eclipse.jgit.lib.Ref.Storage.NETWORK;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
-
-import java.io.IOException;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.dircache.DirCacheEntry;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.SymbolicRef;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevTag;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.transport.ReceiveCommand.Result;
-
-/**
- * Command to create, update or delete an entry inside a
- * {@link org.eclipse.jgit.internal.storage.reftree.RefTree}.
- * <p>
- * Unlike {@link org.eclipse.jgit.transport.ReceiveCommand} (which can only
- * update a reference to an {@link org.eclipse.jgit.lib.ObjectId}), a RefTree
- * Command can also create, modify or delete symbolic references to a target
- * reference.
- * <p>
- * RefTree Commands may wrap a {@code ReceiveCommand} to allow callers to
- * process an existing ReceiveCommand against a RefTree.
- * <p>
- * Commands should be passed into
- * {@link org.eclipse.jgit.internal.storage.reftree.RefTree#apply(java.util.Collection)}
- * for processing.
- */
-public class Command {
- /**
- * Set unprocessed commands as failed due to transaction aborted.
- * <p>
- * If a command is still
- * {@link org.eclipse.jgit.transport.ReceiveCommand.Result#NOT_ATTEMPTED} it
- * will be set to
- * {@link org.eclipse.jgit.transport.ReceiveCommand.Result#REJECTED_OTHER_REASON}.
- * If {@code why} is non-null its contents will be used as the message for
- * the first command status.
- *
- * @param commands
- * commands to mark as failed.
- * @param why
- * optional message to set on the first aborted command.
- */
- public static void abort(Iterable<Command> commands, @Nullable String why) {
- if (why == null || why.isEmpty()) {
- why = JGitText.get().transactionAborted;
- }
- for (Command c : commands) {
- if (c.getResult() == NOT_ATTEMPTED) {
- c.setResult(REJECTED_OTHER_REASON, why);
- why = JGitText.get().transactionAborted;
- }
- }
- }
-
- private final Ref oldRef;
- private final Ref newRef;
- private final ReceiveCommand cmd;
- private Result result;
-
- /**
- * Create a command to create, update or delete a reference.
- * <p>
- * At least one of {@code oldRef} or {@code newRef} must be supplied.
- *
- * @param oldRef
- * expected value. Null if the ref should not exist.
- * @param newRef
- * desired value, must be peeled if not null and not symbolic.
- * Null to delete the ref.
- */
- public Command(@Nullable Ref oldRef, @Nullable Ref newRef) {
- this.oldRef = oldRef;
- this.newRef = newRef;
- this.cmd = null;
- this.result = NOT_ATTEMPTED;
-
- if (oldRef == null && newRef == null) {
- throw new IllegalArgumentException();
- }
- if (newRef != null && !newRef.isPeeled() && !newRef.isSymbolic()) {
- throw new IllegalArgumentException();
- }
- if (oldRef != null && newRef != null
- && !oldRef.getName().equals(newRef.getName())) {
- throw new IllegalArgumentException();
- }
- }
-
- /**
- * Construct a RefTree command wrapped around a ReceiveCommand.
- *
- * @param rw
- * walk instance to peel the {@code newId}.
- * @param cmd
- * command received from a push client.
- * @throws org.eclipse.jgit.errors.MissingObjectException
- * {@code oldId} or {@code newId} is missing.
- * @throws java.io.IOException
- * {@code oldId} or {@code newId} cannot be peeled.
- */
- public Command(RevWalk rw, ReceiveCommand cmd)
- throws MissingObjectException, IOException {
- this.oldRef = toRef(rw, cmd.getOldId(), cmd.getOldSymref(),
- cmd.getRefName(), false);
- this.newRef = toRef(rw, cmd.getNewId(), cmd.getNewSymref(),
- cmd.getRefName(), true);
- this.cmd = cmd;
- }
-
- static Ref toRef(RevWalk rw, ObjectId id, @Nullable String target,
- String name, boolean mustExist)
- throws MissingObjectException, IOException {
- if (target != null) {
- return new SymbolicRef(name,
- new ObjectIdRef.Unpeeled(NETWORK, target, id));
- } else if (ObjectId.zeroId().equals(id)) {
- return null;
- }
-
- try {
- RevObject o = rw.parseAny(id);
- if (o instanceof RevTag) {
- RevObject p = rw.peel(o);
- return new ObjectIdRef.PeeledTag(NETWORK, name, id, p.copy());
- }
- return new ObjectIdRef.PeeledNonTag(NETWORK, name, id);
- } catch (MissingObjectException e) {
- if (mustExist) {
- throw e;
- }
- return new ObjectIdRef.Unpeeled(NETWORK, name, id);
- }
- }
-
- /**
- * Get name of the reference affected by this command.
- *
- * @return name of the reference affected by this command.
- */
- public String getRefName() {
- if (cmd != null) {
- return cmd.getRefName();
- } else if (newRef != null) {
- return newRef.getName();
- }
- return oldRef.getName();
- }
-
- /**
- * Set the result of this command.
- *
- * @param result
- * the command result.
- */
- public void setResult(Result result) {
- setResult(result, null);
- }
-
- /**
- * Set the result of this command.
- *
- * @param result
- * the command result.
- * @param why
- * optional message explaining the result status.
- */
- public void setResult(Result result, @Nullable String why) {
- if (cmd != null) {
- cmd.setResult(result, why);
- } else {
- this.result = result;
- }
- }
-
- /**
- * Get result of executing this command.
- *
- * @return result of executing this command.
- */
- public Result getResult() {
- return cmd != null ? cmd.getResult() : result;
- }
-
- /**
- * Get optional message explaining command failure.
- *
- * @return optional message explaining command failure.
- */
- @Nullable
- public String getMessage() {
- return cmd != null ? cmd.getMessage() : null;
- }
-
- /**
- * Old peeled reference.
- *
- * @return the old reference; null if the command is creating the reference.
- */
- @Nullable
- public Ref getOldRef() {
- return oldRef;
- }
-
- /**
- * New peeled reference.
- *
- * @return the new reference; null if the command is deleting the reference.
- */
- @Nullable
- public Ref getNewRef() {
- return newRef;
- }
-
- /** {@inheritDoc} */
- @Override
- public String toString() {
- StringBuilder s = new StringBuilder();
- append(s, oldRef, "CREATE"); //$NON-NLS-1$
- s.append(' ');
- append(s, newRef, "DELETE"); //$NON-NLS-1$
- s.append(' ').append(getRefName());
- s.append(' ').append(getResult());
- if (getMessage() != null) {
- s.append(' ').append(getMessage());
- }
- return s.toString();
- }
-
- private static void append(StringBuilder s, Ref r, String nullName) {
- if (r == null) {
- s.append(nullName);
- } else if (r.isSymbolic()) {
- s.append(r.getTarget().getName());
- } else {
- ObjectId id = r.getObjectId();
- if (id != null) {
- s.append(id.name());
- }
- }
- }
-
- /**
- * Check the entry is consistent with either the old or the new ref.
- *
- * @param entry
- * current entry; null if the entry does not exist.
- * @return true if entry matches {@link #getOldRef()} or
- * {@link #getNewRef()}; otherwise false.
- */
- boolean checkRef(@Nullable DirCacheEntry entry) {
- if (entry != null && entry.getRawMode() == 0) {
- entry = null;
- }
- return check(entry, oldRef) || check(entry, newRef);
- }
-
- private static boolean check(@Nullable DirCacheEntry cur,
- @Nullable Ref exp) {
- if (cur == null) {
- // Does not exist, ok if oldRef does not exist.
- return exp == null;
- } else if (exp == null) {
- // Expected to not exist, but currently exists, fail.
- return false;
- }
-
- if (exp.isSymbolic()) {
- String dst = exp.getTarget().getName();
- return cur.getRawMode() == TYPE_SYMLINK
- && cur.getObjectId().equals(symref(dst));
- }
-
- return cur.getRawMode() == TYPE_GITLINK
- && cur.getObjectId().equals(exp.getObjectId());
- }
-
- static ObjectId symref(String s) {
- @SuppressWarnings("resource")
- ObjectInserter.Formatter fmt = new ObjectInserter.Formatter();
- return fmt.idFor(OBJ_BLOB, encode(s));
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTree.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTree.java
deleted file mode 100644
index 6f12e9ccb6..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTree.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.storage.reftree;
-
-import static org.eclipse.jgit.lib.Constants.HEAD;
-import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
-import static org.eclipse.jgit.lib.Constants.R_REFS;
-import static org.eclipse.jgit.lib.Constants.encode;
-import static org.eclipse.jgit.lib.FileMode.GITLINK;
-import static org.eclipse.jgit.lib.FileMode.SYMLINK;
-import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
-import static org.eclipse.jgit.lib.FileMode.TYPE_SYMLINK;
-import static org.eclipse.jgit.lib.Ref.Storage.NEW;
-import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
-import static org.eclipse.jgit.lib.RefDatabase.MAX_SYMBOLIC_REF_DEPTH;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.LOCK_FAILURE;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheBuilder;
-import org.eclipse.jgit.dircache.DirCacheEditor;
-import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath;
-import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
-import org.eclipse.jgit.dircache.DirCacheEntry;
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.errors.DirCacheNameConflictException;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.SymbolicRef;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.util.RawParseUtils;
-
-/**
- * Tree of references in the reference graph.
- * <p>
- * The root corresponds to the {@code "refs/"} subdirectory, for example the
- * default reference {@code "refs/heads/master"} is stored at path
- * {@code "heads/master"} in a {@code RefTree}.
- * <p>
- * Normal references are stored as {@link org.eclipse.jgit.lib.FileMode#GITLINK}
- * tree entries. The ObjectId in the tree entry is the ObjectId the reference
- * refers to.
- * <p>
- * Symbolic references are stored as
- * {@link org.eclipse.jgit.lib.FileMode#SYMLINK} entries, with the blob storing
- * the name of the target reference.
- * <p>
- * Annotated tags also store the peeled object using a {@code GITLINK} entry
- * with the suffix <code>" ^"</code> (space carrot), for example
- * {@code "tags/v1.0"} stores the annotated tag object, while
- * <code>"tags/v1.0 ^"</code> stores the commit the tag annotates.
- * <p>
- * {@code HEAD} is a special case and stored as {@code "..HEAD"}.
- */
-public class RefTree {
- /** Suffix applied to GITLINK to indicate its the peeled value of a tag. */
- public static final String PEELED_SUFFIX = " ^"; //$NON-NLS-1$
- static final String ROOT_DOTDOT = ".."; //$NON-NLS-1$
-
- /**
- * Create an empty reference tree.
- *
- * @return a new empty reference tree.
- */
- public static RefTree newEmptyTree() {
- return new RefTree(DirCache.newInCore());
- }
-
- /**
- * Load a reference tree.
- *
- * @param reader
- * reader to scan the reference tree with.
- * @param tree
- * the tree to read.
- * @return the ref tree read from the commit.
- * @throws java.io.IOException
- * the repository cannot be accessed through the reader.
- * @throws org.eclipse.jgit.errors.CorruptObjectException
- * a tree object is corrupt and cannot be read.
- * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
- * a tree object wasn't actually a tree.
- * @throws org.eclipse.jgit.errors.MissingObjectException
- * a reference tree object doesn't exist.
- */
- public static RefTree read(ObjectReader reader, RevTree tree)
- throws MissingObjectException, IncorrectObjectTypeException,
- CorruptObjectException, IOException {
- return new RefTree(DirCache.read(reader, tree));
- }
-
- private DirCache contents;
- private Map<ObjectId, String> pendingBlobs;
-
- private RefTree(DirCache dc) {
- this.contents = dc;
- }
-
- /**
- * Read one reference.
- * <p>
- * References are always returned peeled
- * ({@link org.eclipse.jgit.lib.Ref#isPeeled()} is true). If the reference
- * points to an annotated tag, the returned reference will be peeled and
- * contain {@link org.eclipse.jgit.lib.Ref#getPeeledObjectId()}.
- * <p>
- * If the reference is a symbolic reference and the chain depth is less than
- * {@link org.eclipse.jgit.lib.RefDatabase#MAX_SYMBOLIC_REF_DEPTH} the
- * returned reference is resolved. If the chain depth is longer, the
- * symbolic reference is returned without resolving.
- *
- * @param reader
- * to access objects necessary to read the requested reference.
- * @param name
- * name of the reference to read.
- * @return the reference; null if it does not exist.
- * @throws java.io.IOException
- * cannot read a symbolic reference target.
- */
- @Nullable
- public Ref exactRef(ObjectReader reader, String name) throws IOException {
- Ref r = readRef(reader, name);
- if (r == null) {
- return null;
- } else if (r.isSymbolic()) {
- return resolve(reader, r, 0);
- }
-
- DirCacheEntry p = contents.getEntry(peeledPath(name));
- if (p != null && p.getRawMode() == TYPE_GITLINK) {
- return new ObjectIdRef.PeeledTag(PACKED, r.getName(),
- r.getObjectId(), p.getObjectId());
- }
- return r;
- }
-
- private Ref readRef(ObjectReader reader, String name) throws IOException {
- DirCacheEntry e = contents.getEntry(refPath(name));
- return e != null ? toRef(reader, e, name) : null;
- }
-
- private Ref toRef(ObjectReader reader, DirCacheEntry e, String name)
- throws IOException {
- int mode = e.getRawMode();
- if (mode == TYPE_GITLINK) {
- ObjectId id = e.getObjectId();
- return new ObjectIdRef.PeeledNonTag(PACKED, name, id);
- }
-
- if (mode == TYPE_SYMLINK) {
- ObjectId id = e.getObjectId();
- String n = pendingBlobs != null ? pendingBlobs.get(id) : null;
- if (n == null) {
- byte[] bin = reader.open(id, OBJ_BLOB).getCachedBytes();
- n = RawParseUtils.decode(bin);
- }
- Ref dst = new ObjectIdRef.Unpeeled(NEW, n, null);
- return new SymbolicRef(name, dst);
- }
-
- return null; // garbage file or something; not a reference.
- }
-
- private Ref resolve(ObjectReader reader, Ref ref, int depth)
- throws IOException {
- if (ref.isSymbolic() && depth < MAX_SYMBOLIC_REF_DEPTH) {
- Ref r = readRef(reader, ref.getTarget().getName());
- if (r == null) {
- return ref;
- }
- Ref dst = resolve(reader, r, depth + 1);
- return new SymbolicRef(ref.getName(), dst);
- }
- return ref;
- }
-
- /**
- * Attempt a batch of commands against this RefTree.
- * <p>
- * The batch is applied atomically, either all commands apply at once, or
- * they all reject and the RefTree is left unmodified.
- * <p>
- * On success (when this method returns {@code true}) the command results
- * are left as-is (probably {@code NOT_ATTEMPTED}). Result fields are set
- * only when this method returns {@code false} to indicate failure.
- *
- * @param cmdList
- * to apply. All commands should still have result NOT_ATTEMPTED.
- * @return true if the commands applied; false if they were rejected.
- */
- public boolean apply(Collection<Command> cmdList) {
- try {
- DirCacheEditor ed = contents.editor();
- for (Command cmd : cmdList) {
- if (!isValidRef(cmd)) {
- cmd.setResult(REJECTED_OTHER_REASON,
- JGitText.get().funnyRefname);
- Command.abort(cmdList, null);
- return false;
- }
- apply(ed, cmd);
- }
- ed.finish();
- return true;
- } catch (DirCacheNameConflictException e) {
- String r1 = refName(e.getPath1());
- String r2 = refName(e.getPath2());
- for (Command cmd : cmdList) {
- if (r1.equals(cmd.getRefName())
- || r2.equals(cmd.getRefName())) {
- cmd.setResult(LOCK_FAILURE);
- break;
- }
- }
- Command.abort(cmdList, null);
- return false;
- } catch (LockFailureException e) {
- Command.abort(cmdList, null);
- return false;
- }
- }
-
- private static boolean isValidRef(Command cmd) {
- String n = cmd.getRefName();
- return HEAD.equals(n) || Repository.isValidRefName(n);
- }
-
- private void apply(DirCacheEditor ed, Command cmd) {
- String path = refPath(cmd.getRefName());
- Ref oldRef = cmd.getOldRef();
- final Ref newRef = cmd.getNewRef();
-
- if (newRef == null) {
- checkRef(contents.getEntry(path), cmd);
- ed.add(new DeletePath(path));
- cleanupPeeledRef(ed, oldRef);
- return;
- }
-
- if (newRef.isSymbolic()) {
- final String dst = newRef.getTarget().getName();
- ed.add(new PathEdit(path) {
- @Override
- public void apply(DirCacheEntry ent) {
- checkRef(ent, cmd);
- ObjectId id = Command.symref(dst);
- ent.setFileMode(SYMLINK);
- ent.setObjectId(id);
- if (pendingBlobs == null) {
- pendingBlobs = new HashMap<>(4);
- }
- pendingBlobs.put(id, dst);
- }
- }.setReplace(false));
- cleanupPeeledRef(ed, oldRef);
- return;
- }
-
- ed.add(new PathEdit(path) {
- @Override
- public void apply(DirCacheEntry ent) {
- checkRef(ent, cmd);
- ent.setFileMode(GITLINK);
- ent.setObjectId(newRef.getObjectId());
- }
- }.setReplace(false));
-
- if (newRef.getPeeledObjectId() != null) {
- ed.add(new PathEdit(peeledPath(newRef.getName())) {
- @Override
- public void apply(DirCacheEntry ent) {
- ent.setFileMode(GITLINK);
- ent.setObjectId(newRef.getPeeledObjectId());
- }
- }.setReplace(false));
- } else {
- cleanupPeeledRef(ed, oldRef);
- }
- }
-
- private static void checkRef(@Nullable DirCacheEntry ent, Command cmd) {
- if (!cmd.checkRef(ent)) {
- cmd.setResult(LOCK_FAILURE);
- throw new LockFailureException();
- }
- }
-
- private static void cleanupPeeledRef(DirCacheEditor ed, Ref ref) {
- if (ref != null && !ref.isSymbolic()
- && (!ref.isPeeled() || ref.getPeeledObjectId() != null)) {
- ed.add(new DeletePath(peeledPath(ref.getName())));
- }
- }
-
- /**
- * Convert a path name in a RefTree to the reference name known by Git.
- *
- * @param path
- * name read from the RefTree structure, for example
- * {@code "heads/master"}.
- * @return reference name for the path, {@code "refs/heads/master"}.
- */
- public static String refName(String path) {
- if (path.startsWith(ROOT_DOTDOT)) {
- return path.substring(2);
- }
- return R_REFS + path;
- }
-
- static String refPath(String name) {
- if (name.startsWith(R_REFS)) {
- return name.substring(R_REFS.length());
- }
- return ROOT_DOTDOT + name;
- }
-
- private static String peeledPath(String name) {
- return refPath(name) + PEELED_SUFFIX;
- }
-
- /**
- * Write this reference tree.
- *
- * @param inserter
- * inserter to use when writing trees to the object database.
- * Caller is responsible for flushing the inserter before trying
- * to read the objects, or exposing them through a reference.
- * @return the top level tree.
- * @throws java.io.IOException
- * a tree could not be written.
- */
- public ObjectId writeTree(ObjectInserter inserter) throws IOException {
- if (pendingBlobs != null) {
- for (String s : pendingBlobs.values()) {
- inserter.insert(OBJ_BLOB, encode(s));
- }
- pendingBlobs = null;
- }
- return contents.writeTree(inserter);
- }
-
- /**
- * Create a deep copy of this RefTree.
- *
- * @return a deep copy of this RefTree.
- */
- public RefTree copy() {
- RefTree r = new RefTree(DirCache.newInCore());
- DirCacheBuilder b = r.contents.builder();
- for (int i = 0; i < contents.getEntryCount(); i++) {
- b.add(new DirCacheEntry(contents.getEntry(i)));
- }
- b.finish();
- if (pendingBlobs != null) {
- r.pendingBlobs = new HashMap<>(pendingBlobs);
- }
- return r;
- }
-
- private static class LockFailureException extends RuntimeException {
- private static final long serialVersionUID = 1L;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeBatch.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeBatch.java
deleted file mode 100644
index b154b95adc..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeBatch.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.storage.reftree;
-
-import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
-import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE;
-import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD;
-
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-/** Batch update a {@link RefTreeDatabase}. */
-class RefTreeBatch extends BatchRefUpdate {
- private final RefTreeDatabase refdb;
- private Ref src;
- private ObjectId parentCommitId;
- private ObjectId parentTreeId;
- private RefTree tree;
- private PersonIdent author;
- private ObjectId newCommitId;
-
- RefTreeBatch(RefTreeDatabase refdb) {
- super(refdb);
- this.refdb = refdb;
- }
-
- /** {@inheritDoc} */
- @Override
- public void execute(RevWalk rw, ProgressMonitor monitor)
- throws IOException {
- List<Command> todo = new ArrayList<>(getCommands().size());
- for (ReceiveCommand c : getCommands()) {
- if (!isAllowNonFastForwards()) {
- if (c.getType() == UPDATE) {
- c.updateType(rw);
- }
- if (c.getType() == UPDATE_NONFASTFORWARD) {
- c.setResult(REJECTED_NONFASTFORWARD);
- if (isAtomic()) {
- ReceiveCommand.abort(getCommands());
- return;
- }
- continue;
- }
- }
- todo.add(new Command(rw, c));
- }
- init(rw);
- execute(rw, todo);
- }
-
- void init(RevWalk rw) throws IOException {
- src = refdb.getBootstrap().exactRef(refdb.getTxnCommitted());
- if (src != null && src.getObjectId() != null) {
- RevCommit c = rw.parseCommit(src.getObjectId());
- parentCommitId = c;
- parentTreeId = c.getTree();
- tree = RefTree.read(rw.getObjectReader(), c.getTree());
- } else {
- parentCommitId = ObjectId.zeroId();
- try (ObjectInserter.Formatter fmt = new ObjectInserter.Formatter()) {
- parentTreeId = fmt.idFor(OBJ_TREE, new byte[] {});
- }
- tree = RefTree.newEmptyTree();
- }
- }
-
- @Nullable
- Ref exactRef(ObjectReader reader, String name) throws IOException {
- return tree.exactRef(reader, name);
- }
-
- /**
- * Execute an update from {@link RefTreeUpdate} or {@link RefTreeRename}.
- *
- * @param rw
- * current RevWalk handling the update or rename.
- * @param todo
- * commands to execute. Must never be a bootstrap reference name.
- * @throws IOException
- * the storage system is unable to read or write data.
- */
- void execute(RevWalk rw, List<Command> todo) throws IOException {
- for (Command c : todo) {
- if (c.getResult() != NOT_ATTEMPTED) {
- Command.abort(todo, null);
- return;
- }
- if (refdb.conflictsWithBootstrap(c.getRefName())) {
- c.setResult(REJECTED_OTHER_REASON, MessageFormat
- .format(JGitText.get().invalidRefName, c.getRefName()));
- Command.abort(todo, null);
- return;
- }
- }
-
- if (apply(todo) && newCommitId != null) {
- commit(rw, todo);
- }
- }
-
- private boolean apply(List<Command> todo) throws IOException {
- if (!tree.apply(todo)) {
- // apply set rejection information on commands.
- return false;
- }
-
- Repository repo = refdb.getRepository();
- try (ObjectInserter ins = repo.newObjectInserter()) {
- CommitBuilder b = new CommitBuilder();
- b.setTreeId(tree.writeTree(ins));
- if (parentTreeId.equals(b.getTreeId())) {
- for (Command c : todo) {
- c.setResult(OK);
- }
- return true;
- }
- if (!parentCommitId.equals(ObjectId.zeroId())) {
- b.setParentId(parentCommitId);
- }
-
- author = getRefLogIdent();
- if (author == null) {
- author = new PersonIdent(repo);
- }
- b.setAuthor(author);
- b.setCommitter(author);
- b.setMessage(getRefLogMessage());
- newCommitId = ins.insert(b);
- ins.flush();
- }
- return true;
- }
-
- private void commit(RevWalk rw, List<Command> todo) throws IOException {
- ReceiveCommand commit = new ReceiveCommand(
- parentCommitId, newCommitId,
- refdb.getTxnCommitted());
- updateBootstrap(rw, commit);
-
- if (commit.getResult() == OK) {
- for (Command c : todo) {
- c.setResult(OK);
- }
- } else {
- Command.abort(todo, commit.getResult().name());
- }
- }
-
- private void updateBootstrap(RevWalk rw, ReceiveCommand commit)
- throws IOException {
- BatchRefUpdate u = refdb.getBootstrap().newBatchUpdate();
- u.setAllowNonFastForwards(true);
- u.setPushCertificate(getPushCertificate());
- if (isRefLogDisabled()) {
- u.disableRefLog();
- } else {
- u.setRefLogIdent(author);
- u.setRefLogMessage(getRefLogMessage(), false);
- }
- u.addCommand(commit);
- u.execute(rw, NullProgressMonitor.INSTANCE);
- }
-}
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
deleted file mode 100644
index 34b8f2cd36..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.storage.reftree;
-
-import static org.eclipse.jgit.lib.Constants.HEAD;
-import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
-import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Ref.Storage;
-import org.eclipse.jgit.lib.RefDatabase;
-import org.eclipse.jgit.lib.RefRename;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.SymbolicRef;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevTag;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.util.RefList;
-import org.eclipse.jgit.util.RefMap;
-
-/**
- * Reference database backed by a
- * {@link org.eclipse.jgit.internal.storage.reftree.RefTree}.
- * <p>
- * The storage for RefTreeDatabase has two parts. The main part is a native Git
- * tree object stored under the {@code refs/txn} namespace. To avoid cycles,
- * references to {@code refs/txn} are not stored in that tree object, but
- * instead in a "bootstrap" layer, which is a separate
- * {@link org.eclipse.jgit.lib.RefDatabase} such as
- * {@link org.eclipse.jgit.internal.storage.file.RefDirectory} using local
- * reference files inside of {@code $GIT_DIR/refs}.
- */
-public class RefTreeDatabase extends RefDatabase {
- private final Repository repo;
- private final RefDatabase bootstrap;
- private final String txnCommitted;
-
- @Nullable
- private final String txnNamespace;
- private volatile Scanner.Result refs;
-
- /**
- * Create a RefTreeDb for a repository.
- *
- * @param repo
- * the repository using references in this database.
- * @param bootstrap
- * bootstrap reference database storing the references that
- * anchor the
- * {@link org.eclipse.jgit.internal.storage.reftree.RefTree}.
- */
- public RefTreeDatabase(Repository repo, RefDatabase bootstrap) {
- Config cfg = repo.getConfig();
- String committed = cfg.getString("reftree", null, "committedRef"); //$NON-NLS-1$ //$NON-NLS-2$
- if (committed == null || committed.isEmpty()) {
- committed = "refs/txn/committed"; //$NON-NLS-1$
- }
-
- this.repo = repo;
- this.bootstrap = bootstrap;
- this.txnNamespace = initNamespace(committed);
- this.txnCommitted = committed;
- }
-
- /**
- * Create a RefTreeDb for a repository.
- *
- * @param repo
- * the repository using references in this database.
- * @param bootstrap
- * bootstrap reference database storing the references that
- * anchor the
- * {@link org.eclipse.jgit.internal.storage.reftree.RefTree}.
- * @param txnCommitted
- * name of the bootstrap reference holding the committed RefTree.
- */
- public RefTreeDatabase(Repository repo, RefDatabase bootstrap,
- String txnCommitted) {
- this.repo = repo;
- this.bootstrap = bootstrap;
- this.txnNamespace = initNamespace(txnCommitted);
- this.txnCommitted = txnCommitted;
- }
-
- private static String initNamespace(String committed) {
- int s = committed.lastIndexOf('/');
- if (s < 0) {
- return null;
- }
- return committed.substring(0, s + 1); // Keep trailing '/'.
- }
-
- Repository getRepository() {
- return repo;
- }
-
- /**
- * Get the bootstrap reference database
- *
- * @return the bootstrap reference database, which must be used to access
- * {@link #getTxnCommitted()}, {@link #getTxnNamespace()}.
- */
- public RefDatabase getBootstrap() {
- return bootstrap;
- }
-
- /**
- * Get name of bootstrap reference anchoring committed RefTree.
- *
- * @return name of bootstrap reference anchoring committed RefTree.
- */
- public String getTxnCommitted() {
- return txnCommitted;
- }
-
- /**
- * Get namespace used by bootstrap layer.
- *
- * @return namespace used by bootstrap layer, e.g. {@code refs/txn/}. Always
- * ends in {@code '/'}.
- */
- @Nullable
- public String getTxnNamespace() {
- return txnNamespace;
- }
-
- /** {@inheritDoc} */
- @Override
- public void create() throws IOException {
- bootstrap.create();
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean performsAtomicTransactions() {
- return true;
- }
-
- /** {@inheritDoc} */
- @Override
- public void refresh() {
- bootstrap.refresh();
- }
-
- /** {@inheritDoc} */
- @Override
- public void close() {
- refs = null;
- bootstrap.close();
- }
-
- /** {@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.
- return bootstrap.exactRef(name);
- } else if (conflictsWithBootstrap(name)) {
- return null;
- }
-
- boolean partial = false;
- Ref src = bootstrap.exactRef(txnCommitted);
- Scanner.Result c = refs;
- if (c == null || !c.refTreeId.equals(idOf(src))) {
- c = Scanner.scanRefTree(repo, src, prefixOf(name), false);
- partial = true;
- }
-
- Ref r = c.all.get(name);
- if (r != null && r.isSymbolic()) {
- r = c.sym.get(name);
- if (partial && r.getObjectId() == null) {
- // Attempting exactRef("HEAD") with partial scan will leave
- // an unresolved symref as its target e.g. refs/heads/master
- // was not read by the partial scan. Scan everything instead.
- return getRefs(ALL).get(name);
- }
- }
- return r;
- }
-
- private static String prefixOf(String name) {
- int s = name.lastIndexOf('/');
- if (s >= 0) {
- return name.substring(0, s);
- }
- return ""; //$NON-NLS-1$
- }
-
- /** {@inheritDoc} */
- @Override
- public Map<String, Ref> getRefs(String prefix) throws IOException {
- if (!prefix.isEmpty() && prefix.charAt(prefix.length() - 1) != '/') {
- return new HashMap<>(0);
- }
-
- Ref src = bootstrap.exactRef(txnCommitted);
- Scanner.Result c = refs;
- if (c == null || !c.refTreeId.equals(idOf(src))) {
- c = Scanner.scanRefTree(repo, src, prefix, true);
- if (prefix.isEmpty()) {
- refs = c;
- }
- }
- return new RefMap(prefix, RefList.<Ref> emptyList(), c.all, c.sym);
- }
-
- private static ObjectId idOf(@Nullable Ref src) {
- return src != null && src.getObjectId() != null
- ? src.getObjectId()
- : ObjectId.zeroId();
- }
-
- /** {@inheritDoc} */
- @Override
- public List<Ref> getAdditionalRefs() throws IOException {
- Collection<Ref> txnRefs;
- if (txnNamespace != null) {
- txnRefs = bootstrap.getRefsByPrefix(txnNamespace);
- } else {
- Ref r = bootstrap.exactRef(txnCommitted);
- if (r != null && r.getObjectId() != null) {
- txnRefs = Collections.singleton(r);
- } else {
- txnRefs = Collections.emptyList();
- }
- }
-
- List<Ref> otherRefs = bootstrap.getAdditionalRefs();
- List<Ref> all = new ArrayList<>(txnRefs.size() + otherRefs.size());
- all.addAll(txnRefs);
- all.addAll(otherRefs);
- return all;
- }
-
- /** {@inheritDoc} */
- @Override
- public Ref peel(Ref ref) throws IOException {
- Ref i = ref.getLeaf();
- ObjectId id = i.getObjectId();
- if (i.isPeeled() || id == null) {
- return ref;
- }
- try (RevWalk rw = new RevWalk(repo)) {
- RevObject obj = rw.parseAny(id);
- if (obj instanceof RevTag) {
- ObjectId p = rw.peel(obj).copy();
- i = new ObjectIdRef.PeeledTag(PACKED, i.getName(), id, p);
- } else {
- i = new ObjectIdRef.PeeledNonTag(PACKED, i.getName(), id);
- }
- }
- return recreate(ref, i);
- }
-
- private static Ref recreate(Ref old, Ref leaf) {
- if (old.isSymbolic()) {
- Ref dst = recreate(old.getTarget(), leaf);
- return new SymbolicRef(old.getName(), dst);
- }
- return leaf;
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isNameConflicting(String name) throws IOException {
- return conflictsWithBootstrap(name)
- || !getConflictingNames(name).isEmpty();
- }
-
- /** {@inheritDoc} */
- @Override
- public BatchRefUpdate newBatchUpdate() {
- return new RefTreeBatch(this);
- }
-
- /** {@inheritDoc} */
- @Override
- public RefUpdate newUpdate(String name, boolean detach) throws IOException {
- if (!repo.isBare() && name.indexOf('/') < 0 && !HEAD.equals(name)) {
- return bootstrap.newUpdate(name, detach);
- }
- if (conflictsWithBootstrap(name)) {
- return new AlwaysFailUpdate(this, name);
- }
-
- Ref r = exactRef(name);
- if (r == null) {
- r = new ObjectIdRef.Unpeeled(Storage.NEW, name, null);
- }
-
- boolean detaching = detach && r.isSymbolic();
- if (detaching) {
- r = new ObjectIdRef.Unpeeled(LOOSE, name, r.getObjectId());
- }
-
- RefTreeUpdate u = new RefTreeUpdate(this, r);
- if (detaching) {
- u.setDetachingSymbolicRef();
- }
- return u;
- }
-
- /** {@inheritDoc} */
- @Override
- public RefRename newRename(String fromName, String toName)
- throws IOException {
- RefUpdate from = newUpdate(fromName, true);
- RefUpdate to = newUpdate(toName, true);
- return new RefTreeRename(this, from, to);
- }
-
- boolean conflictsWithBootstrap(String name) {
- if (txnNamespace != null && name.startsWith(txnNamespace)) {
- return true;
- } else if (txnCommitted.equals(name)) {
- return true;
- }
-
- if (name.indexOf('/') < 0 && !HEAD.equals(name)) {
- return true;
- }
-
- if (name.length() > txnCommitted.length()
- && name.charAt(txnCommitted.length()) == '/'
- && name.startsWith(txnCommitted)) {
- return true;
- }
- return false;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeNames.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeNames.java
deleted file mode 100644
index eec0da29e2..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeNames.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.storage.reftree;
-
-import org.eclipse.jgit.lib.RefDatabase;
-
-/**
- * Magic reference name logic for RefTrees.
- */
-public class RefTreeNames {
- /**
- * Suffix used on a {@link RefTreeDatabase#getTxnNamespace()} for user data.
- * <p>
- * A {@link RefTreeDatabase}'s namespace may include a subspace (e.g.
- * {@code "refs/txn/stage/"}) containing commit objects from the usual user
- * portion of the repository (e.g. {@code "refs/heads/"}). These should be
- * packed by the garbage collector alongside other user content rather than
- * with the RefTree.
- */
- private static final String STAGE = "stage/"; //$NON-NLS-1$
-
- /**
- * Determine if the reference is likely to be a RefTree.
- *
- * @param refdb
- * database instance.
- * @param ref
- * reference name.
- * @return {@code true} if the reference is a RefTree.
- */
- public static boolean isRefTree(RefDatabase refdb, String ref) {
- if (refdb instanceof RefTreeDatabase) {
- RefTreeDatabase b = (RefTreeDatabase) refdb;
- if (ref.equals(b.getTxnCommitted())) {
- return true;
- }
-
- String namespace = b.getTxnNamespace();
- if (namespace != null
- && ref.startsWith(namespace)
- && !ref.startsWith(namespace + STAGE)) {
- return true;
- }
- }
- return false;
- }
-
- private RefTreeNames() {
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeRename.java
deleted file mode 100644
index ccf8f75e7d..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeRename.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.storage.reftree;
-
-import static org.eclipse.jgit.lib.Constants.HEAD;
-import static org.eclipse.jgit.lib.RefUpdate.Result.REJECTED;
-import static org.eclipse.jgit.lib.RefUpdate.Result.RENAMED;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefRename;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
-import org.eclipse.jgit.lib.SymbolicRef;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-/** Single reference rename to {@link RefTreeDatabase}. */
-class RefTreeRename extends RefRename {
- private final RefTreeDatabase refdb;
-
- RefTreeRename(RefTreeDatabase refdb, RefUpdate src, RefUpdate dst) {
- super(src, dst);
- this.refdb = refdb;
- }
-
- /** {@inheritDoc} */
- @Override
- protected Result doRename() throws IOException {
- try (RevWalk rw = new RevWalk(refdb.getRepository())) {
- RefTreeBatch batch = new RefTreeBatch(refdb);
- batch.setRefLogIdent(getRefLogIdent());
- batch.setRefLogMessage(getRefLogMessage(), false);
- batch.init(rw);
-
- Ref head = batch.exactRef(rw.getObjectReader(), HEAD);
- Ref oldRef = batch.exactRef(rw.getObjectReader(), source.getName());
- if (oldRef == null) {
- return REJECTED;
- }
-
- Ref newRef = asNew(oldRef);
- List<Command> mv = new ArrayList<>(3);
- mv.add(new Command(oldRef, null));
- mv.add(new Command(null, newRef));
- if (head != null && head.isSymbolic()
- && head.getTarget().getName().equals(oldRef.getName())) {
- mv.add(new Command(
- head,
- new SymbolicRef(head.getName(), newRef)));
- }
- batch.execute(rw, mv);
- return RefTreeUpdate.translate(mv.get(1).getResult(), RENAMED);
- }
- }
-
- private Ref asNew(Ref src) {
- String name = destination.getName();
- if (src.isSymbolic()) {
- return new SymbolicRef(name, src.getTarget());
- }
-
- ObjectId peeled = src.getPeeledObjectId();
- if (peeled != null) {
- return new ObjectIdRef.PeeledTag(
- src.getStorage(),
- name,
- src.getObjectId(),
- peeled);
- }
-
- return new ObjectIdRef.PeeledNonTag(
- src.getStorage(),
- name,
- src.getObjectId());
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeUpdate.java
deleted file mode 100644
index 6e6ccd9986..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeUpdate.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.storage.reftree;
-
-import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
-import static org.eclipse.jgit.lib.Ref.Storage.NEW;
-
-import java.io.IOException;
-import java.util.Collections;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDatabase;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.SymbolicRef;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevTag;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-/** Single reference update to {@link RefTreeDatabase}. */
-class RefTreeUpdate extends RefUpdate {
- private final RefTreeDatabase refdb;
- private RevWalk rw;
- private RefTreeBatch batch;
- private Ref oldRef;
-
- RefTreeUpdate(RefTreeDatabase refdb, Ref ref) {
- super(ref);
- this.refdb = refdb;
- setCheckConflicting(false); // Done automatically by doUpdate.
- }
-
- /** {@inheritDoc} */
- @Override
- protected RefDatabase getRefDatabase() {
- return refdb;
- }
-
- /** {@inheritDoc} */
- @Override
- protected Repository getRepository() {
- return refdb.getRepository();
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean tryLock(boolean deref) throws IOException {
- rw = new RevWalk(getRepository());
- batch = new RefTreeBatch(refdb);
- batch.init(rw);
- oldRef = batch.exactRef(rw.getObjectReader(), getName());
- if (oldRef != null && oldRef.getObjectId() != null) {
- setOldObjectId(oldRef.getObjectId());
- } else if (oldRef == null && getExpectedOldObjectId() != null) {
- setOldObjectId(ObjectId.zeroId());
- }
- return true;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void unlock() {
- batch = null;
- if (rw != null) {
- rw.close();
- rw = null;
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected Result doUpdate(Result desiredResult) throws IOException {
- return run(newRef(getName(), getNewObjectId()), desiredResult);
- }
-
- private Ref newRef(String name, ObjectId id)
- throws MissingObjectException, IOException {
- RevObject o = rw.parseAny(id);
- if (o instanceof RevTag) {
- RevObject p = rw.peel(o);
- return new ObjectIdRef.PeeledTag(LOOSE, name, id, p.copy());
- }
- return new ObjectIdRef.PeeledNonTag(LOOSE, name, id);
- }
-
- /** {@inheritDoc} */
- @Override
- protected Result doDelete(Result desiredResult) throws IOException {
- return run(null, desiredResult);
- }
-
- /** {@inheritDoc} */
- @Override
- protected Result doLink(String target) throws IOException {
- Ref dst = new ObjectIdRef.Unpeeled(NEW, target, null);
- SymbolicRef n = new SymbolicRef(getName(), dst);
- Result desiredResult = getRef().getStorage() == NEW
- ? Result.NEW
- : Result.FORCED;
- return run(n, desiredResult);
- }
-
- private Result run(@Nullable Ref newRef, Result desiredResult)
- throws IOException {
- Command c = new Command(oldRef, newRef);
- batch.setRefLogIdent(getRefLogIdent());
- batch.setRefLogMessage(getRefLogMessage(), isRefLogIncludingResult());
- batch.execute(rw, Collections.singletonList(c));
- return translate(c.getResult(), desiredResult);
- }
-
- static Result translate(ReceiveCommand.Result r, Result desiredResult) {
- switch (r) {
- case OK:
- return desiredResult;
-
- case LOCK_FAILURE:
- return Result.LOCK_FAILURE;
-
- case NOT_ATTEMPTED:
- return Result.NOT_ATTEMPTED;
-
- case REJECTED_MISSING_OBJECT:
- return Result.IO_FAILURE;
-
- case REJECTED_CURRENT_BRANCH:
- return Result.REJECTED_CURRENT_BRANCH;
-
- case REJECTED_OTHER_REASON:
- case REJECTED_NOCREATE:
- case REJECTED_NODELETE:
- case REJECTED_NONFASTFORWARD:
- default:
- return Result.REJECTED;
- }
- }
-}
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
deleted file mode 100644
index 3f5122911c..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Scanner.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 2016, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-package org.eclipse.jgit.internal.storage.reftree;
-
-import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
-import static org.eclipse.jgit.lib.Constants.R_REFS;
-import static org.eclipse.jgit.lib.Constants.encode;
-import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
-import static org.eclipse.jgit.lib.FileMode.TYPE_SYMLINK;
-import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
-import static org.eclipse.jgit.lib.Ref.Storage.NEW;
-import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
-import static org.eclipse.jgit.lib.RefDatabase.MAX_SYMBOLIC_REF_DEPTH;
-
-import java.io.IOException;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.SymbolicRef;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.treewalk.AbstractTreeIterator;
-import org.eclipse.jgit.treewalk.CanonicalTreeParser;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.util.Paths;
-import org.eclipse.jgit.util.RawParseUtils;
-import org.eclipse.jgit.util.RefList;
-
-/** A tree parser that extracts references from a {@link RefTree}. */
-class Scanner {
- private static final int MAX_SYMLINK_BYTES = 10 << 10;
- private static final byte[] BINARY_R_REFS = encode(R_REFS);
- private static final byte[] REFS_DOT_DOT = encode("refs/.."); //$NON-NLS-1$
-
- static class Result {
- final ObjectId refTreeId;
- final RefList<Ref> all;
- final RefList<Ref> sym;
-
- Result(ObjectId id, RefList<Ref> all, RefList<Ref> sym) {
- this.refTreeId = id;
- this.all = all;
- this.sym = sym;
- }
- }
-
- /**
- * Scan a {@link RefTree} and parse entries into {@link Ref} instances.
- *
- * @param repo
- * source repository containing the commit and tree objects that
- * make up the RefTree.
- * @param src
- * bootstrap reference such as {@code refs/txn/committed} to read
- * the reference tree tip from. The current ObjectId will be
- * included in {@link Result#refTreeId}.
- * @param prefix
- * if non-empty a reference prefix to scan only a subdirectory.
- * For example {@code prefix = "refs/heads/"} will limit the scan
- * to only the {@code "heads"} directory of the RefTree, avoiding
- * other directories like {@code "tags"}. Empty string reads all
- * entries in the RefTree.
- * @param recursive
- * if true recurse into subdirectories of the reference tree;
- * false to read only one level. Callers may use false during an
- * implementation of {@code exactRef(String)} where only one
- * reference is needed out of a specific subtree.
- * @return sorted list of references after parsing.
- * @throws IOException
- * tree cannot be accessed from the repository.
- */
- static Result scanRefTree(Repository repo, @Nullable Ref src, String prefix,
- boolean recursive) throws IOException {
- RefList.Builder<Ref> all = new RefList.Builder<>();
- RefList.Builder<Ref> sym = new RefList.Builder<>();
-
- ObjectId srcId;
- if (src != null && src.getObjectId() != null) {
- try (ObjectReader reader = repo.newObjectReader()) {
- srcId = src.getObjectId();
- scan(reader, srcId, prefix, recursive, all, sym);
- }
- } else {
- srcId = ObjectId.zeroId();
- }
-
- RefList<Ref> aList = all.toRefList();
- for (int idx = 0; idx < sym.size();) {
- Ref s = sym.get(idx);
- Ref r = resolve(s, 0, aList);
- if (r != null) {
- sym.set(idx++, r);
- } else {
- // Remove broken symbolic reference, they don't exist.
- sym.remove(idx);
- int rm = aList.find(s.getName());
- if (0 <= rm) {
- aList = aList.remove(rm);
- }
- }
- }
- return new Result(srcId, aList, sym.toRefList());
- }
-
- private static void scan(ObjectReader reader, AnyObjectId srcId,
- String prefix, boolean recursive,
- RefList.Builder<Ref> all, RefList.Builder<Ref> sym)
- throws IncorrectObjectTypeException, IOException {
- CanonicalTreeParser p = createParserAtPath(reader, srcId, prefix);
- if (p == null) {
- return;
- }
-
- while (!p.eof()) {
- int mode = p.getEntryRawMode();
- if (mode == TYPE_TREE) {
- if (recursive) {
- p = p.createSubtreeIterator(reader);
- } else {
- p = p.next();
- }
- continue;
- }
-
- if (!curElementHasPeelSuffix(p)) {
- Ref r = toRef(reader, mode, p);
- if (r != null) {
- all.add(r);
- if (r.isSymbolic()) {
- sym.add(r);
- }
- }
- } else if (mode == TYPE_GITLINK) {
- peel(all, p);
- }
- p = p.next();
- }
- }
-
- private static CanonicalTreeParser createParserAtPath(ObjectReader reader,
- AnyObjectId srcId, String prefix) throws IOException {
- ObjectId root = toTree(reader, srcId);
- if (prefix.isEmpty()) {
- return new CanonicalTreeParser(BINARY_R_REFS, reader, root);
- }
-
- String dir = RefTree.refPath(Paths.stripTrailingSeparator(prefix));
- TreeWalk tw = TreeWalk.forPath(reader, dir, root);
- if (tw == null || !tw.isSubtree()) {
- return null;
- }
-
- ObjectId id = tw.getObjectId(0);
- return new CanonicalTreeParser(encode(prefix), reader, id);
- }
-
- private static Ref resolve(Ref ref, int depth, RefList<Ref> refs)
- throws IOException {
- if (!ref.isSymbolic()) {
- return ref;
- } else if (MAX_SYMBOLIC_REF_DEPTH <= depth) {
- return null;
- }
-
- Ref r = refs.get(ref.getTarget().getName());
- if (r == null) {
- return ref;
- }
-
- Ref dst = resolve(r, depth + 1, refs);
- if (dst == null) {
- return null;
- }
- return new SymbolicRef(ref.getName(), dst);
- }
-
- private static RevTree toTree(ObjectReader reader, AnyObjectId id)
- throws IOException {
- try (RevWalk rw = new RevWalk(reader)) {
- return rw.parseTree(id);
- }
- }
-
- private static boolean curElementHasPeelSuffix(AbstractTreeIterator itr) {
- int n = itr.getEntryPathLength();
- byte[] c = itr.getEntryPathBuffer();
- return n > 2 && c[n - 2] == ' ' && c[n - 1] == '^';
- }
-
- private static void peel(RefList.Builder<Ref> all, CanonicalTreeParser p) {
- String name = refName(p, true);
- for (int idx = all.size() - 1; 0 <= idx; idx--) {
- Ref r = all.get(idx);
- int cmp = r.getName().compareTo(name);
- if (cmp == 0) {
- all.set(idx, new ObjectIdRef.PeeledTag(PACKED, r.getName(),
- r.getObjectId(), p.getEntryObjectId()));
- break;
- } else if (cmp < 0) {
- // Stray peeled name without matching base name; skip entry.
- break;
- }
- }
- }
-
- private static Ref toRef(ObjectReader reader, int mode,
- CanonicalTreeParser p) throws IOException {
- if (mode == TYPE_GITLINK) {
- String name = refName(p, false);
- ObjectId id = p.getEntryObjectId();
- return new ObjectIdRef.PeeledNonTag(PACKED, name, id);
-
- } else if (mode == TYPE_SYMLINK) {
- ObjectId id = p.getEntryObjectId();
- byte[] bin = reader.open(id, OBJ_BLOB)
- .getCachedBytes(MAX_SYMLINK_BYTES);
- String dst = RawParseUtils.decode(bin);
- Ref trg = new ObjectIdRef.Unpeeled(NEW, dst, null);
- String name = refName(p, false);
- return new SymbolicRef(name, trg);
- }
- return null;
- }
-
- private static String refName(CanonicalTreeParser p, boolean peel) {
- byte[] buf = p.getEntryPathBuffer();
- int len = p.getEntryPathLength();
- if (peel) {
- len -= 2;
- }
- int ptr = 0;
- if (RawParseUtils.match(buf, ptr, REFS_DOT_DOT) > 0) {
- ptr = 7;
- }
- return RawParseUtils.decode(buf, ptr, len);
- }
-
- private Scanner() {
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
index e51995f93d..b2242a11ca 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
@@ -28,6 +28,8 @@ import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.internal.JGitText;
@@ -38,6 +40,7 @@ import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.StringUtils;
import org.eclipse.jgit.util.SystemReader;
/**
@@ -107,6 +110,8 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
private File workTree;
+ private String initialBranch = Constants.MASTER;
+
/** Directories limiting the search for a Git repository. */
private List<File> ceilingDirectories;
@@ -350,6 +355,43 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
}
/**
+ * Set the initial branch of the new repository. If not specified
+ * ({@code null} or empty), fall back to the default name (currently
+ * master).
+ *
+ * @param branch
+ * initial branch name of the new repository. If {@code null} or
+ * empty the configured default branch will be used.
+ * @return {@code this}
+ * @throws InvalidRefNameException
+ * if the branch name is not valid
+ *
+ * @since 5.11
+ */
+ public B setInitialBranch(String branch) throws InvalidRefNameException {
+ if (StringUtils.isEmptyOrNull(branch)) {
+ this.initialBranch = Constants.MASTER;
+ } else {
+ if (!Repository.isValidRefName(Constants.R_HEADS + branch)) {
+ throw new InvalidRefNameException(MessageFormat
+ .format(JGitText.get().branchNameInvalid, branch));
+ }
+ this.initialBranch = branch;
+ }
+ return self();
+ }
+
+ /**
+ * Get the initial branch of the new repository.
+ *
+ * @return the initial branch of the new repository.
+ * @since 5.11
+ */
+ public @NonNull String getInitialBranch() {
+ return initialBranch;
+ }
+
+ /**
* Read standard Git environment variables and configure from those.
* <p>
* This method tries to read the standard Git environment variables, such as
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 4f93fda49f..1665f051e9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
- * Copyright (C) 2006-2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2007, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2006, 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006, 2020, Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -16,14 +16,11 @@ 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;
import org.eclipse.jgit.util.References;
/**
@@ -37,7 +34,7 @@ import org.eclipse.jgit.util.References;
* and obtain a {@link org.eclipse.jgit.revwalk.RevCommit} instance by calling
* {@link org.eclipse.jgit.revwalk.RevWalk#parseCommit(AnyObjectId)}.
*/
-public class CommitBuilder {
+public class CommitBuilder extends ObjectBuilder {
private static final ObjectId[] EMPTY_OBJECTID_LIST = new ObjectId[0];
private static final byte[] htree = Constants.encodeASCII("tree"); //$NON-NLS-1$
@@ -50,28 +47,17 @@ public class CommitBuilder {
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;
private ObjectId[] parentIds;
- private PersonIdent author;
-
private PersonIdent committer;
- private GpgSignature gpgSignature;
-
- private String message;
-
- private Charset encoding;
-
/**
* Initialize an empty commit.
*/
public CommitBuilder() {
parentIds = EMPTY_OBJECTID_LIST;
- encoding = UTF_8;
}
/**
@@ -98,8 +84,9 @@ public class CommitBuilder {
*
* @return the author of this commit (who wrote it).
*/
+ @Override
public PersonIdent getAuthor() {
- return author;
+ return super.getAuthor();
}
/**
@@ -108,8 +95,9 @@ public class CommitBuilder {
* @param newAuthor
* the new author. Should not be null.
*/
+ @Override
public void setAuthor(PersonIdent newAuthor) {
- author = newAuthor;
+ super.setAuthor(newAuthor);
}
/**
@@ -132,38 +120,6 @@ 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.
@@ -239,25 +195,6 @@ public class CommitBuilder {
}
/**
- * Get the complete commit message.
- *
- * @return the complete commit message.
- */
- public String getMessage() {
- return message;
- }
-
- /**
- * Set the commit message.
- *
- * @param newMessage
- * the commit message. Should not be null.
- */
- public void setMessage(String newMessage) {
- message = newMessage;
- }
-
- /**
* Set the encoding for the commit information.
*
* @param encodingName
@@ -267,37 +204,10 @@ public class CommitBuilder {
*/
@Deprecated
public void setEncoding(String encodingName) {
- encoding = Charset.forName(encodingName);
- }
-
- /**
- * Set the encoding for the commit information.
- *
- * @param enc
- * the encoding to use.
- */
- public void setEncoding(Charset enc) {
- encoding = enc;
+ setEncoding(Charset.forName(encodingName));
}
- /**
- * Get the encoding that should be used for the commit message text.
- *
- * @return the encoding that should be used for the commit message text.
- */
- public Charset getEncoding() {
- return encoding;
- }
-
- /**
- * Format this builder's state as a commit object.
- *
- * @return this object in the canonical commit format, suitable for storage
- * in a repository.
- * @throws java.io.UnsupportedEncodingException
- * the encoding specified by {@link #getEncoding()} is not
- * supported by this Java runtime.
- */
+ @Override
public byte[] build() throws UnsupportedEncodingException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
OutputStreamWriter w = new OutputStreamWriter(os, getEncoding());
@@ -326,19 +236,16 @@ public class CommitBuilder {
w.flush();
os.write('\n');
- if (getGpgSignature() != null) {
+ GpgSignature signature = getGpgSignature();
+ if (signature != null) {
os.write(hgpgsig);
os.write(' ');
- writeGpgSignatureString(getGpgSignature().toExternalString(), os);
+ writeMultiLineHeader(signature.toExternalString(), os,
+ true);
os.write('\n');
}
- if (!References.isSameObject(getEncoding(), UTF_8)) {
- os.write(hencoding);
- os.write(' ');
- os.write(Constants.encodeASCII(getEncoding().name()));
- os.write('\n');
- }
+ writeEncoding(getEncoding(), os);
os.write('\n');
@@ -356,58 +263,6 @@ 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. A trailing line break is
- * <em>not</em> written; the caller is supposed to terminate the GPG
- * signature header by writing a single newline.
- * </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 {
- int length = in.length();
- for (int i = 0; i < length; ++i) {
- char ch = in.charAt(i);
- switch (ch) {
- case '\r':
- if (i + 1 < length && in.charAt(i + 1) == '\n') {
- ++i;
- }
- if (i + 1 < length) {
- out.write('\n');
- out.write(' ');
- }
- break;
- case '\n':
- if (i + 1 < length) {
- out.write('\n');
- out.write(' ');
- }
- break;
- default:
- // sanity check
- if (ch > 127)
- throw new IllegalArgumentException(MessageFormat
- .format(JGitText.get().notASCIIString, in));
- out.write(ch);
- break;
- }
- }
- }
-
- /**
* Format this builder's state as a commit object.
*
* @return this object in the canonical commit format, suitable for storage
@@ -439,7 +294,7 @@ public class CommitBuilder {
}
r.append("author ");
- r.append(author != null ? author.toString() : "NOT_SET");
+ r.append(getAuthor() != null ? getAuthor().toString() : "NOT_SET");
r.append("\n");
r.append("committer ");
@@ -447,17 +302,20 @@ public class CommitBuilder {
r.append("\n");
r.append("gpgSignature ");
- r.append(gpgSignature != null ? gpgSignature.toString() : "NOT_SET");
+ GpgSignature signature = getGpgSignature();
+ r.append(signature != null ? signature.toString()
+ : "NOT_SET");
r.append("\n");
- if (encoding != null && !References.isSameObject(encoding, UTF_8)) {
+ Charset encoding = getEncoding();
+ if (!References.isSameObject(encoding, UTF_8)) {
r.append("encoding ");
r.append(encoding.name());
r.append("\n");
}
r.append("\n");
- r.append(message != null ? message : "");
+ r.append(getMessage() != null ? getMessage() : "");
r.append("}");
return r.toString();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 4fcf8e2dcd..03c1ef904c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -93,13 +93,27 @@ public final class ConfigConstants {
public static final String CONFIG_GPG_SECTION = "gpg";
/**
+ * The "protocol" section
+ * @since 5.9
+ */
+ public static final String CONFIG_PROTOCOL_SECTION = "protocol";
+
+ /**
* The "format" key
* @since 5.2
*/
public static final String CONFIG_KEY_FORMAT = "format";
/**
+ * The "program" key
+ *
+ * @since 5.11
+ */
+ public static final String CONFIG_KEY_PROGRAM = "program";
+
+ /**
* The "signingKey" key
+ *
* @since 5.2
*/
public static final String CONFIG_KEY_SIGNINGKEY = "signingKey";
@@ -111,13 +125,29 @@ public final class ConfigConstants {
public static final String CONFIG_COMMIT_SECTION = "commit";
/**
+ * The "tag" section
+ *
+ * @since 5.11
+ */
+ public static final String CONFIG_TAG_SECTION = "tag";
+
+ /**
* The "gpgSign" key
+ *
* @since 5.2
*/
public static final String CONFIG_KEY_GPGSIGN = "gpgSign";
/**
+ * The "forceSignAnnotated" key
+ *
+ * @since 5.11
+ */
+ public static final String CONFIG_KEY_FORCE_SIGN_ANNOTATED = "forceSignAnnotated";
+
+ /**
* The "hooksPath" key.
+ *
* @since 5.6
*/
public static final String CONFIG_KEY_HOOKS_PATH = "hooksPath";
@@ -532,12 +562,6 @@ public final class ConfigConstants {
public static final String CONFIG_REF_STORAGE_REFTABLE = "reftable";
/**
- * The "reftree" refStorage format
- * @since 5.7
- */
- public static final String CONFIG_REFSTORAGE_REFTREE = "reftree";
-
- /**
* The "jmx" section
* @since 5.1.13
*/
@@ -685,10 +709,23 @@ public final class ConfigConstants {
public static final String CONFIG_INDEX_SECTION = "index";
/**
- * The "index.version" key
+ * The "version" key
*
* @since 5.9
*/
public static final String CONFIG_KEY_VERSION = "version";
+ /**
+ * The "init" section
+ *
+ * @since 5.11
+ */
+ public static final String CONFIG_INIT_SECTION = "init";
+
+ /**
+ * The "defaultBranch" key
+ *
+ * @since 5.11
+ */
+ public static final String CONFIG_KEY_DEFAULT_BRANCH = "defaultbranch";
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
index c1527bc47b..427a235f3b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, Salesforce. and others
+ * Copyright (C) 2018, 2021 Salesforce and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -43,16 +43,65 @@ public class GpgConfig {
}
}
- private final Config config;
+ private final GpgFormat keyFormat;
+
+ private final String signingKey;
+
+ private final String program;
+
+ private final boolean signCommits;
+
+ private final boolean signAllTags;
+
+ private final boolean forceAnnotated;
+
+ /**
+ * Create a {@link GpgConfig} with the given parameters and default
+ * {@code true} for signing commits and {@code false} for tags.
+ *
+ * @param keySpec
+ * to use
+ * @param format
+ * to use
+ * @param gpgProgram
+ * to use
+ * @since 5.11
+ */
+ public GpgConfig(String keySpec, GpgFormat format, String gpgProgram) {
+ keyFormat = format;
+ signingKey = keySpec;
+ program = gpgProgram;
+ signCommits = true;
+ signAllTags = false;
+ forceAnnotated = false;
+ }
/**
- * Create a new GPG config, which will read configuration from config.
+ * Create a new GPG config that reads the configuration from config.
*
* @param config
* the config to read from
*/
public GpgConfig(Config config) {
- this.config = config;
+ keyFormat = config.getEnum(GpgFormat.values(),
+ ConfigConstants.CONFIG_GPG_SECTION, null,
+ ConfigConstants.CONFIG_KEY_FORMAT, GpgFormat.OPENPGP);
+ signingKey = config.getString(ConfigConstants.CONFIG_USER_SECTION, null,
+ ConfigConstants.CONFIG_KEY_SIGNINGKEY);
+
+ String exe = config.getString(ConfigConstants.CONFIG_GPG_SECTION,
+ keyFormat.toConfigValue(), ConfigConstants.CONFIG_KEY_PROGRAM);
+ if (exe == null) {
+ exe = config.getString(ConfigConstants.CONFIG_GPG_SECTION, null,
+ ConfigConstants.CONFIG_KEY_PROGRAM);
+ }
+ program = exe;
+ signCommits = config.getBoolean(ConfigConstants.CONFIG_COMMIT_SECTION,
+ ConfigConstants.CONFIG_KEY_GPGSIGN, false);
+ signAllTags = config.getBoolean(ConfigConstants.CONFIG_TAG_SECTION,
+ ConfigConstants.CONFIG_KEY_GPGSIGN, false);
+ forceAnnotated = config.getBoolean(ConfigConstants.CONFIG_TAG_SECTION,
+ ConfigConstants.CONFIG_KEY_FORCE_SIGN_ANNOTATED, false);
}
/**
@@ -61,9 +110,19 @@ public class GpgConfig {
* @return the {@link org.eclipse.jgit.lib.GpgConfig.GpgFormat}
*/
public GpgFormat getKeyFormat() {
- return config.getEnum(GpgFormat.values(),
- ConfigConstants.CONFIG_GPG_SECTION, null,
- ConfigConstants.CONFIG_KEY_FORMAT, GpgFormat.OPENPGP);
+ return keyFormat;
+ }
+
+ /**
+ * Retrieves the value of the configured GPG program to use, as defined by
+ * gpg.openpgp.program, gpg.x509.program (depending on the defined
+ * {@link #getKeyFormat() format}), or gpg.program.
+ *
+ * @return the program string configured, or {@code null} if none
+ * @since 5.11
+ */
+ public String getProgram() {
+ return program;
}
/**
@@ -72,8 +131,7 @@ public class GpgConfig {
* @return the value of user.signingKey (may be <code>null</code>)
*/
public String getSigningKey() {
- return config.getString(ConfigConstants.CONFIG_USER_SECTION, null,
- ConfigConstants.CONFIG_KEY_SIGNINGKEY);
+ return signingKey;
}
/**
@@ -82,7 +140,29 @@ public class GpgConfig {
* @return the value of commit.gpgSign (defaults to <code>false</code>)
*/
public boolean isSignCommits() {
- return config.getBoolean(ConfigConstants.CONFIG_COMMIT_SECTION,
- ConfigConstants.CONFIG_KEY_GPGSIGN, false);
+ return signCommits;
+ }
+
+ /**
+ * Retrieves the value of git config {@code tag.gpgSign}.
+ *
+ * @return the value of {@code tag.gpgSign}; by default {@code false}
+ *
+ * @since 5.11
+ */
+ public boolean isSignAllTags() {
+ return signAllTags;
+ }
+
+ /**
+ * Retrieves the value of git config {@code tag.forceSignAnnotated}.
+ *
+ * @return the value of {@code tag.forceSignAnnotated}; by default
+ * {@code false}
+ *
+ * @since 5.11
+ */
+ public boolean isSignAnnotated() {
+ return forceAnnotated;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java
new file mode 100644
index 0000000000..074f46567b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+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.UnsupportedSigningFormatException;
+import org.eclipse.jgit.transport.CredentialsProvider;
+
+/**
+ * Creates GPG signatures for Git objects.
+ *
+ * @since 5.11
+ */
+public interface GpgObjectSigner {
+
+ /**
+ * Signs the specified object.
+ *
+ * <p>
+ * Implementors should obtain the payload for signing from the specified
+ * object via {@link ObjectBuilder#build()} and create a proper
+ * {@link GpgSignature}. The generated signature must be set on the
+ * specified {@code object} (see
+ * {@link ObjectBuilder#setGpgSignature(GpgSignature)}).
+ * </p>
+ * <p>
+ * Any existing signature on the object must be discarded prior obtaining
+ * the payload via {@link ObjectBuilder#build()}.
+ * </p>
+ *
+ * @param object
+ * the object to sign (must not be {@code null} 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)
+ * @param config
+ * GPG settings from the git config
+ * @throws CanceledException
+ * when signing was canceled (eg., user aborted when entering
+ * passphrase)
+ * @throws UnsupportedSigningFormatException
+ * if a config is given and the wanted key format is not
+ * supported
+ */
+ void signObject(@NonNull ObjectBuilder object,
+ @Nullable String gpgSigningKey, @NonNull PersonIdent committer,
+ CredentialsProvider credentialsProvider, GpgConfig config)
+ throws CanceledException, UnsupportedSigningFormatException;
+
+ /**
+ * 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)
+ * @param config
+ * GPG settings from the git config
+ * @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)
+ * @throws UnsupportedSigningFormatException
+ * if a config is given and the wanted key format is not
+ * supported
+ */
+ public abstract boolean canLocateSigningKey(@Nullable String gpgSigningKey,
+ @NonNull PersonIdent committer,
+ CredentialsProvider credentialsProvider, GpgConfig config)
+ throws CanceledException, UnsupportedSigningFormatException;
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java
new file mode 100644
index 0000000000..a7a39c998f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+import java.util.Date;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.revwalk.RevObject;
+
+/**
+ * A {@code GpgVerifier} can verify GPG signatures on git commits and tags.
+ *
+ * @since 5.11
+ */
+public interface GpgSignatureVerifier {
+
+ /**
+ * Verifies the signature on a signed commit or tag.
+ *
+ * @param object
+ * to verify
+ * @param config
+ * the {@link GpgConfig} to use
+ * @return a {@link SignatureVerification} describing the outcome of the
+ * verification, or {@code null} if the object was not signed
+ * @throws IOException
+ * if an error occurs getting a public key
+ * @throws org.eclipse.jgit.api.errors.JGitInternalException
+ * if signature verification fails
+ */
+ @Nullable
+ SignatureVerification verifySignature(@NonNull RevObject object,
+ @NonNull GpgConfig config) throws IOException;
+
+
+ /**
+ * Verifies a given signature for given data.
+ *
+ * @param data
+ * the signature is for
+ * @param signatureData
+ * the ASCII-armored signature
+ * @return a {@link SignatureVerification} describing the outcome
+ * @throws IOException
+ * if the signature cannot be parsed
+ * @throws JGitInternalException
+ * if signature verification fails
+ */
+ public SignatureVerification verify(byte[] data, byte[] signatureData)
+ throws IOException;
+
+ /**
+ * Retrieves the name of this verifier. This should be a short string
+ * identifying the engine that verified the signature, like "gpg" if GPG is
+ * used, or "bc" for a BouncyCastle implementation.
+ *
+ * @return the name
+ */
+ @NonNull
+ String getName();
+
+ /**
+ * A {@link GpgSignatureVerifier} may cache public keys to speed up
+ * verifying signatures on multiple objects. This clears this cache, if any.
+ */
+ void clear();
+
+ /**
+ * A {@code SignatureVerification} returns data about a (positively or
+ * negatively) verified signature.
+ */
+ interface SignatureVerification {
+
+ // Data about the signature.
+
+ @NonNull
+ Date getCreationDate();
+
+ // Data from the signature used to find a public key.
+
+ /**
+ * Obtains the signer as stored in the signature, if known.
+ *
+ * @return the signer, or {@code null} if unknown
+ */
+ String getSigner();
+
+ /**
+ * Obtains the short or long fingerprint of the public key as stored in
+ * the signature, if known.
+ *
+ * @return the fingerprint, or {@code null} if unknown
+ */
+ String getKeyFingerprint();
+
+ // Some information about the found public key.
+
+ /**
+ * Obtains the OpenPGP user ID associated with the key.
+ *
+ * @return the user id, or {@code null} if unknown
+ */
+ String getKeyUser();
+
+ /**
+ * Tells whether the public key used for this signature verification was
+ * expired when the signature was created.
+ *
+ * @return {@code true} if the key was expired already, {@code false}
+ * otherwise
+ */
+ boolean isExpired();
+
+ /**
+ * Obtains the trust level of the public key used to verify the
+ * signature.
+ *
+ * @return the trust level
+ */
+ @NonNull
+ TrustLevel getTrustLevel();
+
+ // The verification result.
+
+ /**
+ * Tells whether the signature verification was successful.
+ *
+ * @return {@code true} if the signature was verified successfully;
+ * {@code false} if not.
+ */
+ boolean getVerified();
+
+ /**
+ * Obtains a human-readable message giving additional information about
+ * the outcome of the verification.
+ *
+ * @return the message, or {@code null} if none set.
+ */
+ String getMessage();
+ }
+
+ /**
+ * The owner's trust in a public key.
+ */
+ enum TrustLevel {
+ UNKNOWN, NEVER, MARGINAL, FULL, ULTIMATE
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java
new file mode 100644
index 0000000000..4b1dbedeb1
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.util.Iterator;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@code GpgSignatureVerifierFactory} creates {@link GpgSignatureVerifier} instances.
+ *
+ * @since 5.11
+ */
+public abstract class GpgSignatureVerifierFactory {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(GpgSignatureVerifierFactory.class);
+
+ private static volatile GpgSignatureVerifierFactory defaultFactory = loadDefault();
+
+ private static GpgSignatureVerifierFactory loadDefault() {
+ try {
+ ServiceLoader<GpgSignatureVerifierFactory> loader = ServiceLoader
+ .load(GpgSignatureVerifierFactory.class);
+ Iterator<GpgSignatureVerifierFactory> iter = loader.iterator();
+ if (iter.hasNext()) {
+ return iter.next();
+ }
+ } catch (ServiceConfigurationError e) {
+ LOG.error(e.getMessage(), e);
+ }
+ return null;
+ }
+
+ /**
+ * Retrieves the default factory.
+ *
+ * @return the default factory or {@code null} if none set
+ */
+ public static GpgSignatureVerifierFactory getDefault() {
+ return defaultFactory;
+ }
+
+ /**
+ * Sets the default factory.
+ *
+ * @param factory
+ * the new default factory
+ */
+ public static void setDefault(GpgSignatureVerifierFactory factory) {
+ defaultFactory = factory;
+ }
+
+ /**
+ * Creates a new {@link GpgSignatureVerifier}.
+ *
+ * @return the new {@link GpgSignatureVerifier}
+ */
+ public abstract GpgSignatureVerifier getVerifier();
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectBuilder.java
new file mode 100644
index 0000000000..4b7054f72b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectBuilder.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2020, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.Objects;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.util.References;
+
+/**
+ * Common base class for {@link CommitBuilder} and {@link TagBuilder}.
+ *
+ * @since 5.11
+ */
+public abstract class ObjectBuilder {
+
+ /** Byte representation of "encoding". */
+ private static final byte[] hencoding = Constants.encodeASCII("encoding"); //$NON-NLS-1$
+
+ private PersonIdent author;
+
+ private GpgSignature gpgSignature;
+
+ private String message;
+
+ private Charset encoding = StandardCharsets.UTF_8;
+
+ /**
+ * Retrieves the author of this object.
+ *
+ * @return the author of this object, or {@code null} if not set yet
+ */
+ protected PersonIdent getAuthor() {
+ return author;
+ }
+
+ /**
+ * Sets the author (name, email address, and date) of this object.
+ *
+ * @param newAuthor
+ * the new author, must be non-{@code null}
+ */
+ protected void setAuthor(PersonIdent newAuthor) {
+ author = Objects.requireNonNull(newAuthor);
+ }
+
+ /**
+ * Sets the GPG signature of this object.
+ * <p>
+ * Note, the signature set here will change the payload of the object, 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 gpgSignature
+ * the signature to set or {@code null} to unset
+ * @since 5.3
+ */
+ public void setGpgSignature(@Nullable GpgSignature gpgSignature) {
+ this.gpgSignature = gpgSignature;
+ }
+
+ /**
+ * Retrieves the GPG signature of this object.
+ *
+ * @return the GPG signature of this object, or {@code null} if the object
+ * is not signed
+ * @since 5.3
+ */
+ @Nullable
+ public GpgSignature getGpgSignature() {
+ return gpgSignature;
+ }
+
+ /**
+ * Retrieves the complete message of the object.
+ *
+ * @return the complete message; can be {@code null}.
+ */
+ @Nullable
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Sets the message (commit message, or message of an annotated tag).
+ *
+ * @param message
+ * the message.
+ */
+ public void setMessage(@Nullable String message) {
+ this.message = message;
+ }
+
+ /**
+ * Retrieves the encoding that should be used for the message text.
+ *
+ * @return the encoding that should be used for the message text.
+ */
+ @NonNull
+ public Charset getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Sets the encoding for the object message.
+ *
+ * @param encoding
+ * the encoding to use.
+ */
+ public void setEncoding(@NonNull Charset encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Format this builder's state as a git object.
+ *
+ * @return this object in the canonical git format, suitable for storage in
+ * a repository.
+ * @throws java.io.UnsupportedEncodingException
+ * the encoding specified by {@link #getEncoding()} is not
+ * supported by this Java runtime.
+ */
+ @NonNull
+ public abstract byte[] build() throws UnsupportedEncodingException;
+
+ /**
+ * 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. A trailing line break is
+ * <em>not</em> written; the caller is supposed to terminate the GPG
+ * signature header by writing a single newline.
+ * </p>
+ *
+ * @param in
+ * signature string with line breaks
+ * @param out
+ * output stream
+ * @param enforceAscii
+ * whether to throw {@link IllegalArgumentException} if non-ASCII
+ * characters are encountered
+ * @throws IOException
+ * thrown by the output stream
+ * @throws IllegalArgumentException
+ * if the signature string contains non 7-bit ASCII chars and
+ * {@code enforceAscii == true}
+ */
+ static void writeMultiLineHeader(@NonNull String in,
+ @NonNull OutputStream out, boolean enforceAscii)
+ throws IOException, IllegalArgumentException {
+ int length = in.length();
+ for (int i = 0; i < length; ++i) {
+ char ch = in.charAt(i);
+ switch (ch) {
+ case '\r':
+ if (i + 1 < length && in.charAt(i + 1) == '\n') {
+ ++i;
+ }
+ if (i + 1 < length) {
+ out.write('\n');
+ out.write(' ');
+ }
+ break;
+ case '\n':
+ if (i + 1 < length) {
+ out.write('\n');
+ out.write(' ');
+ }
+ break;
+ default:
+ // sanity check
+ if (ch > 127 && enforceAscii)
+ throw new IllegalArgumentException(MessageFormat
+ .format(JGitText.get().notASCIIString, in));
+ out.write(ch);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Writes an "encoding" header.
+ *
+ * @param encoding
+ * to write
+ * @param out
+ * to write to
+ * @throws IOException
+ * if writing fails
+ */
+ static void writeEncoding(@NonNull Charset encoding,
+ @NonNull OutputStream out) throws IOException {
+ if (!References.isSameObject(encoding, UTF_8)) {
+ out.write(hencoding);
+ out.write(' ');
+ out.write(Constants.encodeASCII(encoding.name()));
+ out.write('\n');
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
index 6bb6ae590a..718ed89142 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
@@ -17,9 +17,18 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.revwalk.BitmappedObjectReachabilityChecker;
+import org.eclipse.jgit.internal.revwalk.BitmappedReachabilityChecker;
+import org.eclipse.jgit.internal.revwalk.PedestrianObjectReachabilityChecker;
+import org.eclipse.jgit.internal.revwalk.PedestrianReachabilityChecker;
+import org.eclipse.jgit.revwalk.ObjectReachabilityChecker;
+import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.ReachabilityChecker;
+import org.eclipse.jgit.revwalk.RevWalk;
/**
* Reads an {@link org.eclipse.jgit.lib.ObjectDatabase} for a single thread.
@@ -408,6 +417,54 @@ public abstract class ObjectReader implements AutoCloseable {
}
/**
+ * Create a reachability checker that will use bitmaps if possible.
+ *
+ * @param rw
+ * revwalk for use by the reachability checker
+ * @return the most efficient reachability checker for this repository.
+ * @throws IOException
+ * if it cannot open any of the underlying indices.
+ *
+ * @since 5.11
+ */
+ @NonNull
+ public ReachabilityChecker createReachabilityChecker(RevWalk rw)
+ throws IOException {
+ if (getBitmapIndex() != null) {
+ return new BitmappedReachabilityChecker(rw);
+ }
+
+ return new PedestrianReachabilityChecker(true, rw);
+ }
+
+ /**
+ * Create an object reachability checker that will use bitmaps if possible.
+ *
+ * This reachability checker accepts any object as target. For checks
+ * exclusively between commits, use
+ * {@link #createReachabilityChecker(RevWalk)}.
+ *
+ * @param ow
+ * objectwalk for use by the reachability checker
+ * @return the most efficient object reachability checker for this
+ * repository.
+ *
+ * @throws IOException
+ * if it cannot open any of the underlying indices.
+ *
+ * @since 5.11
+ */
+ @NonNull
+ public ObjectReachabilityChecker createObjectReachabilityChecker(
+ ObjectWalk ow) throws IOException {
+ if (getBitmapIndex() != null) {
+ return new BitmappedObjectReachabilityChecker(ow);
+ }
+
+ return new PedestrianObjectReachabilityChecker(ow);
+ }
+
+ /**
* Get the {@link org.eclipse.jgit.lib.ObjectInserter} from which this
* reader was created using {@code inserter.newReader()}
*
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 6832c9cd80..7b7bdebac8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -21,6 +21,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
@@ -414,6 +417,31 @@ public abstract class RefDatabase {
}
/**
+ * Returns refs whose names start with a given prefix excluding all refs that
+ * start with one of the given prefixes.
+ *
+ * <p>
+ * The default implementation is not efficient. Implementors of {@link RefDatabase}
+ * should override this method directly if a better implementation is possible.
+ *
+ * @param include string that names of refs should start with; may be empty.
+ * @param excludes strings that names of refs can't start with; may be empty.
+ * @return immutable list of refs whose names start with {@code prefix} and none
+ * of the strings in {@code exclude}.
+ * @throws java.io.IOException the reference space cannot be accessed.
+ * @since 5.11
+ */
+ @NonNull
+ public List<Ref> getRefsByPrefixWithExclusions(String include, Set<String> excludes)
+ throws IOException {
+ Stream<Ref> refs = getRefs(include).values().stream();
+ for(String exclude: excludes) {
+ refs = refs.filter(r -> !r.getName().startsWith(exclude));
+ }
+ return Collections.unmodifiableList(refs.collect(Collectors.toList()));
+ }
+
+ /**
* Returns refs whose names start with one of the given prefixes.
* <p>
* The default implementation uses {@link #getRefsByPrefix(String)}.
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 a7a832c1aa..1e8a6c9175 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -127,6 +127,8 @@ public abstract class Repository implements AutoCloseable {
/** If not bare, the index file caching the working file states. */
private final File indexFile;
+ private final String initialBranch;
+
/**
* Initialize a new repository instance.
*
@@ -138,6 +140,7 @@ public abstract class Repository implements AutoCloseable {
fs = options.getFS();
workTree = options.getWorkTree();
indexFile = options.getIndexFile();
+ initialBranch = options.getInitialBranch();
}
/**
@@ -1034,6 +1037,16 @@ public abstract class Repository implements AutoCloseable {
}
/**
+ * Get the initial branch name of a new repository
+ *
+ * @return the initial branch name of a new repository
+ * @since 5.11
+ */
+ protected @NonNull String getInitialBranch() {
+ return initialBranch;
+ }
+
+ /**
* Objects known to exist but not expressed by {@link #getAllRefs()}.
* <p>
* When a repository borrows objects from another repository, it can
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
index d36ccd5bea..41f291b57b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -14,6 +14,7 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -193,7 +194,7 @@ public class RepositoryCache {
cache.configureEviction(repositoryCacheConfig);
}
- private final ConcurrentHashMap<Key, Repository> cacheMap;
+ private final Map<Key, Repository> cacheMap;
private final Lock[] openLocks;
@@ -201,6 +202,8 @@ public class RepositoryCache {
private volatile long expireAfter;
+ private final Object schedulerLock = new Lock();
+
private RepositoryCache() {
cacheMap = new ConcurrentHashMap<>();
openLocks = new Lock[4];
@@ -214,7 +217,7 @@ public class RepositoryCache {
RepositoryCacheConfig repositoryCacheConfig) {
expireAfter = repositoryCacheConfig.getExpireAfter();
ScheduledThreadPoolExecutor scheduler = WorkQueue.getExecutor();
- synchronized (scheduler) {
+ synchronized (schedulerLock) {
if (cleanupTask != null) {
cleanupTask.cancel(false);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
index 71f01150c9..facb4a54be 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2006-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006, 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
- * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2020, Chris Aniszczyk <caniszczyk@gmail.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -17,8 +17,13 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.util.References;
/**
* Mutable builder to construct an annotated tag recording a project state.
@@ -30,17 +35,22 @@ import org.eclipse.jgit.revwalk.RevObject;
* and obtain a {@link org.eclipse.jgit.revwalk.RevTag} instance by calling
* {@link org.eclipse.jgit.revwalk.RevWalk#parseTag(AnyObjectId)}.
*/
-public class TagBuilder {
+public class TagBuilder extends ObjectBuilder {
+
+ private static final byte[] hobject = Constants.encodeASCII("object"); //$NON-NLS-1$
+
+ private static final byte[] htype = Constants.encodeASCII("type"); //$NON-NLS-1$
+
+ private static final byte[] htag = Constants.encodeASCII("tag"); //$NON-NLS-1$
+
+ private static final byte[] htagger = Constants.encodeASCII("tagger"); //$NON-NLS-1$
+
private ObjectId object;
private int type = Constants.OBJ_BAD;
private String tag;
- private PersonIdent tagger;
-
- private String message;
-
/**
* Get the type of object this tag refers to.
*
@@ -109,7 +119,7 @@ public class TagBuilder {
* @return creator of this tag. May be null.
*/
public PersonIdent getTagger() {
- return tagger;
+ return getAuthor();
}
/**
@@ -119,26 +129,7 @@ public class TagBuilder {
* the creator. May be null.
*/
public void setTagger(PersonIdent taggerIdent) {
- tagger = taggerIdent;
- }
-
- /**
- * Get the complete commit message.
- *
- * @return the complete commit message.
- */
- public String getMessage() {
- return message;
- }
-
- /**
- * Set the tag's message.
- *
- * @param newMessage
- * the tag's message.
- */
- public void setMessage(String newMessage) {
- message = newMessage;
+ setAuthor(taggerIdent);
}
/**
@@ -147,31 +138,65 @@ public class TagBuilder {
* @return this object in the canonical annotated tag format, suitable for
* storage in a repository.
*/
- public byte[] build() {
+ @Override
+ public byte[] build() throws UnsupportedEncodingException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try (OutputStreamWriter w = new OutputStreamWriter(os,
- UTF_8)) {
- w.write("object "); //$NON-NLS-1$
- getObjectId().copyTo(w);
- w.write('\n');
+ getEncoding())) {
- w.write("type "); //$NON-NLS-1$
- w.write(Constants.typeString(getObjectType()));
- w.write("\n"); //$NON-NLS-1$
+ os.write(hobject);
+ os.write(' ');
+ getObjectId().copyTo(os);
+ os.write('\n');
- w.write("tag "); //$NON-NLS-1$
+ os.write(htype);
+ os.write(' ');
+ os.write(Constants
+ .encodeASCII(Constants.typeString(getObjectType())));
+ os.write('\n');
+
+ os.write(htag);
+ os.write(' ');
w.write(getTag());
- w.write("\n"); //$NON-NLS-1$
+ w.flush();
+ os.write('\n');
if (getTagger() != null) {
- w.write("tagger "); //$NON-NLS-1$
+ os.write(htagger);
+ os.write(' ');
w.write(getTagger().toExternalString());
- w.write('\n');
+ w.flush();
+ os.write('\n');
+ }
+
+ writeEncoding(getEncoding(), os);
+
+ os.write('\n');
+ String msg = getMessage();
+ if (msg != null) {
+ w.write(msg);
+ w.flush();
}
- w.write('\n');
- if (getMessage() != null)
- w.write(getMessage());
+ GpgSignature signature = getGpgSignature();
+ if (signature != null) {
+ if (msg != null && !msg.isEmpty() && !msg.endsWith("\n")) { //$NON-NLS-1$
+ // If signed, the message *must* end with a linefeed
+ // character, otherwise signature verification will fail.
+ // (The signature will have been computed over the payload
+ // containing the message without LF, but will be verified
+ // against a payload with the LF.) The signature must start
+ // on a new line.
+ throw new JGitInternalException(
+ JGitText.get().signedTagMessageNoLf);
+ }
+ String externalForm = signature.toExternalString();
+ w.write(externalForm);
+ w.flush();
+ if (!externalForm.endsWith("\n")) { //$NON-NLS-1$
+ os.write('\n');
+ }
+ }
} catch (IOException err) {
// This should never occur, the only way to get it above is
// for the ByteArrayOutputStream to throw, but it doesn't.
@@ -185,10 +210,17 @@ public class TagBuilder {
* Format this builder's state as an annotated tag object.
*
* @return this object in the canonical annotated tag format, suitable for
- * storage in a repository.
+ * storage in a repository, or {@code null} if the tag cannot be
+ * encoded
+ * @deprecated since 5.11; use {@link #build()} instead
*/
+ @Deprecated
public byte[] toByteArray() {
- return build();
+ try {
+ return build();
+ } catch (UnsupportedEncodingException e) {
+ return null;
+ }
}
/** {@inheritDoc} */
@@ -211,14 +243,23 @@ public class TagBuilder {
r.append(tag != null ? tag : "NOT_SET");
r.append("\n");
- if (tagger != null) {
+ if (getTagger() != null) {
r.append("tagger ");
- r.append(tagger);
+ r.append(getTagger());
+ r.append("\n");
+ }
+
+ Charset encoding = getEncoding();
+ if (!References.isSameObject(encoding, UTF_8)) {
+ r.append("encoding ");
+ r.append(encoding.name());
r.append("\n");
}
r.append("\n");
- r.append(message != null ? message : "");
+ r.append(getMessage() != null ? getMessage() : "");
+ GpgSignature signature = getGpgSignature();
+ r.append(signature != null ? signature.toExternalString() : "");
r.append("}");
return r.toString();
}
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 6c217fdf25..4bfb38d286 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -703,18 +703,21 @@ public class ResolveMerger extends ThreeWayMerger {
// conflict between ours and theirs. file/folder conflicts between
// base/index/workingTree and something else are not relevant or
// detected later
- if (nonTree(modeO) && !nonTree(modeT)) {
- if (nonTree(modeB))
- add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
- add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
- unmergedPaths.add(tw.getPathString());
- enterSubtree = false;
- return true;
- }
- if (nonTree(modeT) && !nonTree(modeO)) {
+ if (nonTree(modeO) != nonTree(modeT)) {
+ if (ignoreConflicts) {
+ // In case of merge failures, ignore this path instead of reporting unmerged, so
+ // a caller can use virtual commit. This will not result in files with conflict
+ // markers in the index/working tree. The actual diff on the path will be
+ // computed directly on children.
+ enterSubtree = false;
+ return true;
+ }
if (nonTree(modeB))
add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
- add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
+ if (nonTree(modeO))
+ add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
+ if (nonTree(modeT))
+ add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
unmergedPaths.add(tw.getPathString());
enterSubtree = false;
return true;
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 d7dd3bee52..881873de6f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
@@ -11,6 +11,7 @@
package org.eclipse.jgit.nls;
import java.util.Locale;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jgit.errors.TranslationBundleLoadingException;
@@ -110,7 +111,8 @@ public class NLS {
}
private final Locale locale;
- private final ConcurrentHashMap<Class, TranslationBundle> map = new ConcurrentHashMap<>();
+
+ private final Map<Class, TranslationBundle> map = new ConcurrentHashMap<>();
private NLS(Locale locale) {
this.locale = locale;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java
index b875be9270..0cabf07057 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java
@@ -93,7 +93,7 @@ public class DateRevQueue extends AbstractRevQueue {
head = n;
} else {
Entry p = q.next;
- while (p != null && p.commit.commitTime > when) {
+ while (p != null && p.commit.commitTime >= when) {
q = p;
p = q.next;
}
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 4c7a6f556e..e6f9580bf7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
@@ -172,14 +172,14 @@ public class ObjectWalk extends RevWalk {
* when the index fails to load.
*
* @since 5.8
+ * @deprecated use
+ * {@code ObjectReader#createObjectReachabilityChecker(ObjectWalk)}
+ * instead.
*/
- public ObjectReachabilityChecker createObjectReachabilityChecker()
+ @Deprecated
+ public final ObjectReachabilityChecker createObjectReachabilityChecker()
throws IOException {
- if (reader.getBitmapIndex() != null) {
- return new BitmappedObjectReachabilityChecker(this);
- }
-
- return new PedestrianObjectReachabilityChecker(this);
+ return reader.createObjectReachabilityChecker(this);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
index cac257199f..b9d145008b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2008, 2009, Google Inc.
* Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2021, Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -18,7 +18,9 @@ import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
+import java.util.Arrays;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
@@ -35,6 +37,10 @@ import org.eclipse.jgit.util.StringUtils;
* An annotated tag.
*/
public class RevTag extends RevObject {
+
+ private static final byte[] hSignature = Constants
+ .encodeASCII("-----BEGIN PGP SIGNATURE-----"); //$NON-NLS-1$
+
/**
* Parse an annotated tag from its canonical format.
*
@@ -171,6 +177,71 @@ public class RevTag extends RevObject {
return RawParseUtils.parsePersonIdent(raw, nameB);
}
+ private static int nextStart(byte[] prefix, byte[] buffer, int from) {
+ int stop = buffer.length - prefix.length + 1;
+ int ptr = from;
+ if (ptr > 0) {
+ ptr = RawParseUtils.nextLF(buffer, ptr - 1);
+ }
+ while (ptr < stop) {
+ int lineStart = ptr;
+ boolean found = true;
+ for (byte element : prefix) {
+ if (element != buffer[ptr++]) {
+ found = false;
+ break;
+ }
+ }
+ if (found) {
+ return lineStart;
+ }
+ do {
+ ptr = RawParseUtils.nextLF(buffer, ptr);
+ } while (ptr < stop && buffer[ptr] == '\n');
+ }
+ return -1;
+ }
+
+ private int getSignatureStart() {
+ byte[] raw = buffer;
+ int msgB = RawParseUtils.tagMessage(raw, 0);
+ if (msgB < 0) {
+ return msgB;
+ }
+ // Find the last signature start and return the rest
+ int start = nextStart(hSignature, raw, msgB);
+ if (start < 0) {
+ return start;
+ }
+ int next = RawParseUtils.nextLF(raw, start);
+ while (next < raw.length) {
+ int newStart = nextStart(hSignature, raw, next);
+ if (newStart < 0) {
+ break;
+ }
+ start = newStart;
+ next = RawParseUtils.nextLF(raw, start);
+ }
+ return start;
+ }
+
+ /**
+ * Parse the GPG signature from the raw buffer.
+ *
+ * @return contents of the GPG signature; {@code null} if the tag was not
+ * signed.
+ * @since 5.11
+ */
+ @Nullable
+ public final byte[] getRawGpgSignature() {
+ byte[] raw = buffer;
+ int start = getSignatureStart();
+ if (start < 0) {
+ return null;
+ }
+ return Arrays.copyOfRange(raw, start, raw.length);
+ }
+
/**
* Parse the complete tag message and decode it to a string.
* <p>
@@ -187,7 +258,12 @@ public class RevTag extends RevObject {
if (msgB < 0) {
return ""; //$NON-NLS-1$
}
- return RawParseUtils.decode(guessEncoding(), raw, msgB, raw.length);
+ int signatureStart = getSignatureStart();
+ int end = signatureStart < 0 ? raw.length : signatureStart;
+ if (end == msgB) {
+ return ""; //$NON-NLS-1$
+ }
+ return RawParseUtils.decode(guessEncoding(), raw, msgB, end);
}
/**
@@ -213,6 +289,16 @@ public class RevTag extends RevObject {
}
int msgE = RawParseUtils.endOfParagraph(raw, msgB);
+ int signatureStart = getSignatureStart();
+ if (signatureStart >= msgB && msgE > signatureStart) {
+ msgE = signatureStart;
+ if (msgE > msgB) {
+ msgE--;
+ }
+ if (msgB == msgE) {
+ return ""; //$NON-NLS-1$
+ }
+ }
String str = RawParseUtils.decode(guessEncoding(), raw, msgB, msgE);
if (RevCommit.hasLF(raw, msgB, msgE)) {
str = StringUtils.replaceLineBreaksWithSpace(str);
@@ -258,6 +344,22 @@ public class RevTag extends RevObject {
}
/**
+ * Obtain the raw unparsed tag body (<b>NOTE - THIS IS NOT A COPY</b>).
+ * <p>
+ * This method is exposed only to provide very fast, efficient access to
+ * this tag's message buffer. Applications relying on this buffer should be
+ * very careful to ensure they do not modify its contents during their use
+ * of it.
+ *
+ * @return the raw unparsed tag body. This is <b>NOT A COPY</b>. Do not
+ * alter the returned array.
+ * @since 5.11
+ */
+ public final byte[] getRawBuffer() {
+ return buffer;
+ }
+
+ /**
* Discard the message buffer to reduce memory usage.
* <p>
* After discarding the memory usage of the {@code RevTag} is reduced to
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 6b62fcdf6d..631d861c0d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -236,13 +236,13 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
* if it cannot open any of the underlying indices.
*
* @since 5.4
+ * @deprecated use {@code ObjectReader#createReachabilityChecker(RevWalk)}
+ * instead.
*/
- public ReachabilityChecker createReachabilityChecker() throws IOException {
- if (reader.getBitmapIndex() != null) {
- return new BitmappedReachabilityChecker(this);
- }
-
- return new PedestrianReachabilityChecker(true, this);
+ @Deprecated
+ public final ReachabilityChecker createReachabilityChecker()
+ throws IOException {
+ return reader.createReachabilityChecker(this);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
index 3a36398629..3826bf7401 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
@@ -1,8 +1,8 @@
/*
- * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2008, 2010 Google Inc.
* Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -13,7 +13,12 @@
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.REF_ATTR_PEELED;
+import static org.eclipse.jgit.transport.GitProtocolConstants.REF_ATTR_SYMREF_TARGET;
+import static org.eclipse.jgit.transport.GitProtocolConstants.VERSION_1;
+import static org.eclipse.jgit.transport.GitProtocolConstants.VERSION_2;
import java.io.EOFException;
import java.io.IOException;
@@ -22,23 +27,29 @@ import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.errors.InvalidObjectIdException;
import org.eclipse.jgit.errors.NoRemoteRepositoryException;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.errors.RemoteRepositoryException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.util.StringUtils;
import org.eclipse.jgit.util.io.InterruptTimer;
import org.eclipse.jgit.util.io.TimeoutInputStream;
import org.eclipse.jgit.util.io.TimeoutOutputStream;
@@ -92,17 +103,27 @@ abstract class BasePackConnection extends BaseConnection {
protected boolean statelessRPC;
/** Capability tokens advertised by the remote side. */
- private final Set<String> remoteCapablities = new HashSet<>();
+ private final Map<String, String> remoteCapabilities = new HashMap<>();
/** Extra objects the remote has, but which aren't offered as refs. */
protected final Set<ObjectId> additionalHaves = new HashSet<>();
+ private TransferConfig.ProtocolVersion protocol = TransferConfig.ProtocolVersion.V0;
+
BasePackConnection(PackTransport packTransport) {
transport = (Transport) packTransport;
local = transport.local;
uri = transport.uri;
}
+ TransferConfig.ProtocolVersion getProtocolVersion() {
+ return protocol;
+ }
+
+ void setProtocolVersion(@NonNull TransferConfig.ProtocolVersion protocol) {
+ this.protocol = protocol;
+ }
+
/**
* Configure this connection with the directional pipes.
*
@@ -147,12 +168,15 @@ abstract class BasePackConnection extends BaseConnection {
* {@link #close()} and the exception is wrapped (if necessary) and thrown
* as a {@link org.eclipse.jgit.errors.TransportException}.
*
+ * @return {@code true} if the refs were read; {@code false} otherwise
+ * indicating that {@link #lsRefs} must be called
+ *
* @throws org.eclipse.jgit.errors.TransportException
* the reference list could not be scanned.
*/
- protected void readAdvertisedRefs() throws TransportException {
+ protected boolean readAdvertisedRefs() throws TransportException {
try {
- readAdvertisedRefsImpl();
+ return readAdvertisedRefsImpl();
} catch (TransportException err) {
close();
throw err;
@@ -162,35 +186,79 @@ abstract class BasePackConnection extends BaseConnection {
}
}
- private void readAdvertisedRefsImpl() throws IOException {
- final LinkedHashMap<String, Ref> avail = new LinkedHashMap<>();
- for (;;) {
- String line;
-
- try {
- line = pckIn.readString();
- } catch (EOFException eof) {
- if (avail.isEmpty())
- throw noRepository();
- throw eof;
- }
- if (PacketLineIn.isEnd(line))
- break;
+ private String readLine() throws IOException {
+ String line = pckIn.readString();
+ if (PacketLineIn.isEnd(line)) {
+ return null;
+ }
+ if (line.startsWith("ERR ")) { //$NON-NLS-1$
+ // This is a customized remote service error.
+ // Users should be informed about it.
+ throw new RemoteRepositoryException(uri, line.substring(4));
+ }
+ return line;
+ }
- if (line.startsWith("ERR ")) { //$NON-NLS-1$
- // This is a customized remote service error.
- // Users should be informed about it.
- throw new RemoteRepositoryException(uri, line.substring(4));
- }
+ private boolean readAdvertisedRefsImpl() throws IOException {
+ final Map<String, Ref> avail = new LinkedHashMap<>();
+ final Map<String, String> symRefs = new LinkedHashMap<>();
+ for (boolean first = true;; first = false) {
+ String line;
- if (avail.isEmpty()) {
+ if (first) {
+ boolean isV1 = false;
+ try {
+ line = readLine();
+ } catch (EOFException e) {
+ TransportException noRepo = noRepository();
+ noRepo.initCause(e);
+ throw noRepo;
+ }
+ if (line != null && VERSION_1.equals(line)) {
+ // Same as V0, except for this extra line. We shouldn't get
+ // it since we never request V1.
+ setProtocolVersion(TransferConfig.ProtocolVersion.V0);
+ isV1 = true;
+ line = readLine();
+ }
+ if (line == null) {
+ break;
+ }
final int nul = line.indexOf('\0');
if (nul >= 0) {
- // The first line (if any) may contain "hidden"
- // capability values after a NUL byte.
- remoteCapablities.addAll(
- Arrays.asList(line.substring(nul + 1).split(" "))); //$NON-NLS-1$
+ // Protocol V0: The first line (if any) may contain
+ // "hidden" capability values after a NUL byte.
+ for (String capability : line.substring(nul + 1)
+ .split(" ")) { //$NON-NLS-1$
+ if (capability.startsWith(CAPABILITY_SYMREF_PREFIX)) {
+ String[] parts = capability
+ .substring(
+ CAPABILITY_SYMREF_PREFIX.length())
+ .split(":", 2); //$NON-NLS-1$
+ if (parts.length == 2) {
+ symRefs.put(parts[0], parts[1]);
+ }
+ } else {
+ addCapability(capability);
+ }
+ }
line = line.substring(0, nul);
+ setProtocolVersion(TransferConfig.ProtocolVersion.V0);
+ } else if (!isV1 && VERSION_2.equals(line)) {
+ // Protocol V2: remaining lines are capabilities as
+ // key=value pairs
+ setProtocolVersion(TransferConfig.ProtocolVersion.V2);
+ readCapabilitiesV2();
+ // Break out here so that stateless RPC transports get a
+ // chance to set up the output stream.
+ return false;
+ } else {
+ setProtocolVersion(TransferConfig.ProtocolVersion.V0);
+ }
+ } else {
+ line = readLine();
+ if (line == null) {
+ break;
}
}
@@ -199,73 +267,214 @@ abstract class BasePackConnection extends BaseConnection {
throw invalidRefAdvertisementLine(line);
}
String name = line.substring(41, line.length());
- if (avail.isEmpty() && name.equals("capabilities^{}")) { //$NON-NLS-1$
- // special line from git-receive-pack to show
+ if (first && name.equals("capabilities^{}")) { //$NON-NLS-1$
+ // special line from git-receive-pack (protocol V0) to show
// capabilities when there are no refs to advertise
continue;
}
- final ObjectId id;
- try {
- id = ObjectId.fromString(line.substring(0, 40));
- } catch (InvalidObjectIdException e) {
- PackProtocolException ppe = invalidRefAdvertisementLine(line);
- ppe.initCause(e);
- throw ppe;
- }
+ final ObjectId id = toId(line, line.substring(0, 40));
if (name.equals(".have")) { //$NON-NLS-1$
additionalHaves.add(id);
- } else if (name.endsWith("^{}")) { //$NON-NLS-1$
- name = name.substring(0, name.length() - 3);
- final Ref prior = avail.get(name);
- if (prior == null)
- throw new PackProtocolException(uri, MessageFormat.format(
- JGitText.get().advertisementCameBefore, name, name));
-
- if (prior.getPeeledObjectId() != null)
- throw duplicateAdvertisement(name + "^{}"); //$NON-NLS-1$
-
- avail.put(name, new ObjectIdRef.PeeledTag(
- Ref.Storage.NETWORK, name, prior.getObjectId(), id));
} else {
- final Ref prior = avail.put(name, new ObjectIdRef.PeeledNonTag(
- Ref.Storage.NETWORK, name, id));
- if (prior != null)
- throw duplicateAdvertisement(name);
+ processLineV1(name, id, avail);
}
}
- updateWithSymRefs(avail, extractSymRefsFromCapabilities(remoteCapablities));
+ updateWithSymRefs(avail, symRefs);
available(avail);
+ return true;
}
/**
- * Finds values in the given capabilities of the form:
- *
- * <pre>
- * symref=<em>source</em>:<em>target</em>
- * </pre>
+ * Issue a protocol V2 ls-refs command and read its response.
*
- * And returns a Map of source->target entries.
- *
- * @param capabilities
- * the capabilities lines
- * @return a Map of the symref entries from capabilities
- * @throws NullPointerException
- * if capabilities, or any entry in it, is null
+ * @param refSpecs
+ * to produce ref prefixes from if the server supports git
+ * protocol V2
+ * @param additionalPatterns
+ * to use for ref prefixes if the server supports git protocol V2
+ * @throws TransportException
+ * if the command could not be run or its output not be read
*/
- static Map<String, String> extractSymRefsFromCapabilities(Collection<String> capabilities) {
+ protected void lsRefs(Collection<RefSpec> refSpecs,
+ String... additionalPatterns) throws TransportException {
+ try {
+ lsRefsImpl(refSpecs, additionalPatterns);
+ } catch (TransportException err) {
+ close();
+ throw err;
+ } catch (IOException | RuntimeException err) {
+ close();
+ throw new TransportException(err.getMessage(), err);
+ }
+ }
+
+ private void lsRefsImpl(Collection<RefSpec> refSpecs,
+ String... additionalPatterns) throws IOException {
+ pckOut.writeString("command=" + COMMAND_LS_REFS); //$NON-NLS-1$
+ // Add the user-agent
+ String agent = UserAgent.get();
+ if (agent != null && isCapableOf(OPTION_AGENT)) {
+ pckOut.writeString(OPTION_AGENT + '=' + agent);
+ }
+ pckOut.writeDelim();
+ pckOut.writeString("peel"); //$NON-NLS-1$
+ pckOut.writeString("symrefs"); //$NON-NLS-1$
+ for (String refPrefix : getRefPrefixes(refSpecs, additionalPatterns)) {
+ pckOut.writeString("ref-prefix " + refPrefix); //$NON-NLS-1$
+ }
+ pckOut.end();
+ final Map<String, Ref> avail = new LinkedHashMap<>();
final Map<String, String> symRefs = new LinkedHashMap<>();
- for (String option : capabilities) {
- if (option.startsWith(CAPABILITY_SYMREF_PREFIX)) {
- String[] symRef = option
- .substring(CAPABILITY_SYMREF_PREFIX.length())
- .split(":", 2); //$NON-NLS-1$
- if (symRef.length == 2) {
- symRefs.put(symRef[0], symRef[1]);
+ for (;;) {
+ String line = readLine();
+ if (line == null) {
+ break;
+ }
+ // Expecting to get a line in the form "sha1 refname"
+ if (line.length() < 41 || line.charAt(40) != ' ') {
+ throw invalidRefAdvertisementLine(line);
+ }
+ String name = line.substring(41, line.length());
+ final ObjectId id = toId(line, line.substring(0, 40));
+ if (name.equals(".have")) { //$NON-NLS-1$
+ additionalHaves.add(id);
+ } else {
+ processLineV2(line, id, name, avail, symRefs);
+ }
+ }
+ updateWithSymRefs(avail, symRefs);
+ available(avail);
+ }
+
+ private Collection<String> getRefPrefixes(Collection<RefSpec> refSpecs,
+ String... additionalPatterns) {
+ if (refSpecs.isEmpty() && (additionalPatterns == null
+ || additionalPatterns.length == 0)) {
+ return Collections.emptyList();
+ }
+ Set<String> patterns = new HashSet<>();
+ if (additionalPatterns != null) {
+ Arrays.stream(additionalPatterns).filter(Objects::nonNull)
+ .forEach(patterns::add);
+ }
+ for (RefSpec spec : refSpecs) {
+ // TODO: for now we only do protocol V2 for fetch. For push
+ // RefSpecs, the logic would need to be different. (At the
+ // minimum, take spec.getDestination().)
+ String src = spec.getSource();
+ if (ObjectId.isId(src)) {
+ continue;
+ }
+ if (spec.isWildcard()) {
+ patterns.add(src.substring(0, src.indexOf('*')));
+ } else {
+ patterns.add(src);
+ patterns.add(Constants.R_REFS + src);
+ patterns.add(Constants.R_HEADS + src);
+ patterns.add(Constants.R_TAGS + src);
+ }
+ }
+ return patterns;
+ }
+
+ private void readCapabilitiesV2() throws IOException {
+ // In git protocol V2, capabilities are different. If it's a key-value
+ // pair, the key may be a command name, and the value a space-separated
+ // list of capabilities for that command. We still store it in the same
+ // map as for protocol v0/v1. Protocol v2 code has to account for this.
+ for (;;) {
+ String line = readLine();
+ if (line == null) {
+ break;
+ }
+ addCapability(line);
+ }
+ }
+
+ private void addCapability(String capability) {
+ String parts[] = capability.split("=", 2); //$NON-NLS-1$
+ if (parts.length == 2) {
+ remoteCapabilities.put(parts[0], parts[1]);
+ }
+ remoteCapabilities.put(capability, null);
+ }
+
+ private ObjectId toId(String line, String value)
+ throws PackProtocolException {
+ try {
+ return ObjectId.fromString(value);
+ } catch (InvalidObjectIdException e) {
+ PackProtocolException ppe = invalidRefAdvertisementLine(line);
+ ppe.initCause(e);
+ throw ppe;
+ }
+ }
+
+ private void processLineV1(String name, ObjectId id, Map<String, Ref> avail)
+ throws IOException {
+ if (name.endsWith("^{}")) { //$NON-NLS-1$
+ name = name.substring(0, name.length() - 3);
+ final Ref prior = avail.get(name);
+ if (prior == null) {
+ throw new PackProtocolException(uri, MessageFormat.format(
+ JGitText.get().advertisementCameBefore, name, name));
+ }
+ if (prior.getPeeledObjectId() != null) {
+ throw duplicateAdvertisement(name + "^{}"); //$NON-NLS-1$
+ }
+ avail.put(name, new ObjectIdRef.PeeledTag(Ref.Storage.NETWORK, name,
+ prior.getObjectId(), id));
+ } else {
+ final Ref prior = avail.put(name, new ObjectIdRef.PeeledNonTag(
+ Ref.Storage.NETWORK, name, id));
+ if (prior != null) {
+ throw duplicateAdvertisement(name);
+ }
+ }
+ }
+
+ private void processLineV2(String line, ObjectId id, String rest,
+ Map<String, Ref> avail, Map<String, String> symRefs)
+ throws IOException {
+ String[] parts = rest.split(" "); //$NON-NLS-1$
+ String name = parts[0];
+ // Two attributes possible, symref-target or peeled
+ String symRefTarget = null;
+ String peeled = null;
+ for (int i = 1; i < parts.length; i++) {
+ if (parts[i].startsWith(REF_ATTR_SYMREF_TARGET)) {
+ if (symRefTarget != null) {
+ throw new PackProtocolException(uri, MessageFormat.format(
+ JGitText.get().duplicateRefAttribute, line));
+ }
+ symRefTarget = parts[i]
+ .substring(REF_ATTR_SYMREF_TARGET.length());
+ } else if (parts[i].startsWith(REF_ATTR_PEELED)) {
+ if (peeled != null) {
+ throw new PackProtocolException(uri, MessageFormat.format(
+ JGitText.get().duplicateRefAttribute, line));
}
+ peeled = parts[i].substring(REF_ATTR_PEELED.length());
}
+ if (peeled != null && symRefTarget != null) {
+ break;
+ }
+ }
+ Ref idRef;
+ if (peeled != null) {
+ idRef = new ObjectIdRef.PeeledTag(Ref.Storage.NETWORK, name, id,
+ toId(line, peeled));
+ } else {
+ idRef = new ObjectIdRef.PeeledNonTag(Ref.Storage.NETWORK, name, id);
+ }
+ Ref prior = avail.put(name, idRef);
+ if (prior != null) {
+ throw duplicateAdvertisement(name);
+ }
+ if (!StringUtils.isEmptyOrNull(symRefTarget)) {
+ symRefs.put(name, symRefTarget);
}
- return symRefs;
}
/**
@@ -334,6 +543,22 @@ abstract class BasePackConnection extends BaseConnection {
}
}
}
+ // If HEAD is still in the symRefs map here, the real ref was not
+ // reported, but we know it must point to the object reported for HEAD.
+ // So fill it in in the refMap.
+ String headRefName = symRefs.get(Constants.HEAD);
+ if (headRefName != null && !refMap.containsKey(headRefName)) {
+ Ref headRef = refMap.get(Constants.HEAD);
+ if (headRef != null) {
+ ObjectId headObj = headRef.getObjectId();
+ headRef = new ObjectIdRef.PeeledNonTag(Ref.Storage.NETWORK,
+ headRefName, headObj);
+ refMap.put(headRefName, headRef);
+ headRef = new SymbolicRef(Constants.HEAD, headRef);
+ refMap.put(Constants.HEAD, headRef);
+ symRefs.remove(Constants.HEAD);
+ }
+ }
}
/**
@@ -357,7 +582,7 @@ abstract class BasePackConnection extends BaseConnection {
* @return whether this option is supported
*/
protected boolean isCapableOf(String option) {
- return remoteCapablities.contains(option);
+ return remoteCapabilities.containsKey(option);
}
/**
@@ -378,6 +603,17 @@ abstract class BasePackConnection extends BaseConnection {
}
/**
+ * Return a capability value.
+ *
+ * @param option
+ * to get
+ * @return the value stored, if any.
+ */
+ protected String getCapability(String option) {
+ return remoteCapabilities.get(option);
+ }
+
+ /**
* Add user agent capability
*
* @param b
@@ -385,7 +621,7 @@ abstract class BasePackConnection extends BaseConnection {
*/
protected void addUserAgentCapability(StringBuilder b) {
String a = UserAgent.get();
- if (a != null && UserAgent.hasAgent(remoteCapablities)) {
+ if (a != null && remoteCapabilities.get(OPTION_AGENT) != null) {
b.append(' ').append(OPTION_AGENT).append('=').append(a);
}
}
@@ -393,7 +629,8 @@ abstract class BasePackConnection extends BaseConnection {
/** {@inheritDoc} */
@Override
public String getPeerUserAgent() {
- return UserAgent.getAgent(remoteCapablities, super.getPeerUserAgent());
+ String agent = remoteCapabilities.get(OPTION_AGENT);
+ return agent != null ? agent : super.getPeerUserAgent();
}
private PackProtocolException duplicateAdvertisement(String name) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
index a2fb51f46d..d344deac26 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2008, 2010 Google Inc.
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -16,18 +16,21 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Set;
import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.errors.RemoteRepositoryException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.PackLock;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
@@ -44,6 +47,7 @@ import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck;
import org.eclipse.jgit.transport.PacketLineIn.AckNackResult;
+import org.eclipse.jgit.util.StringUtils;
import org.eclipse.jgit.util.TemporaryBuffer;
/**
@@ -207,7 +211,10 @@ public abstract class BasePackFetchConnection extends BasePackConnection
private int maxHaves;
- /** RPC state, if {@link BasePackConnection#statelessRPC} is true. */
+ /**
+ * RPC state, if {@link BasePackConnection#statelessRPC} is true or protocol
+ * V2 is used.
+ */
private TemporaryBuffer.Heap state;
private PacketLineOut pckState;
@@ -321,6 +328,13 @@ public abstract class BasePackFetchConnection extends BasePackConnection
return Collections.<PackLock> emptyList();
}
+ private void clearState() {
+ walk.dispose();
+ reachableCommits = null;
+ state = null;
+ pckState = null;
+ }
+
/**
* Execute common ancestor negotiation and fetch the objects.
*
@@ -349,18 +363,34 @@ public abstract class BasePackFetchConnection extends BasePackConnection
markRefsAdvertised();
markReachable(have, maxTimeWanted(want));
+ if (TransferConfig.ProtocolVersion.V2
+ .equals(getProtocolVersion())) {
+ // Protocol V2 always is a "stateless" protocol, even over a
+ // bidirectional pipe: the server serves one "fetch" request and
+ // then forgets anything it has learned, so the next fetch
+ // request has to re-send all wants and previously determined
+ // common objects as "have"s again.
+ state = new TemporaryBuffer.Heap(Integer.MAX_VALUE);
+ pckState = new PacketLineOut(state);
+ try {
+ doFetchV2(monitor, want, outputStream);
+ } finally {
+ clearState();
+ }
+ return;
+ }
+ // Protocol V0/1
if (statelessRPC) {
state = new TemporaryBuffer.Heap(Integer.MAX_VALUE);
pckState = new PacketLineOut(state);
}
-
- if (sendWants(want)) {
+ PacketLineOut output = statelessRPC ? pckState : pckOut;
+ if (sendWants(want, output)) {
+ output.end();
+ outNeedsEnd = false;
negotiate(monitor);
- walk.dispose();
- reachableCommits = null;
- state = null;
- pckState = null;
+ clearState();
receivePack(monitor, outputStream);
}
@@ -373,6 +403,185 @@ public abstract class BasePackFetchConnection extends BasePackConnection
}
}
+ private void doFetchV2(ProgressMonitor monitor, Collection<Ref> want,
+ OutputStream outputStream) throws IOException, CancelledException {
+ sideband = true;
+ negotiateBegin();
+
+ pckState.writeString("command=" + GitProtocolConstants.COMMAND_FETCH); //$NON-NLS-1$
+ // Capabilities are sent as command arguments in protocol V2
+ String agent = UserAgent.get();
+ if (agent != null && isCapableOf(GitProtocolConstants.OPTION_AGENT)) {
+ pckState.writeString(
+ GitProtocolConstants.OPTION_AGENT + '=' + agent);
+ }
+ Set<String> capabilities = new HashSet<>();
+ String advertised = getCapability(GitProtocolConstants.COMMAND_FETCH);
+ if (!StringUtils.isEmptyOrNull(advertised)) {
+ capabilities.addAll(Arrays.asList(advertised.split("\\s+"))); //$NON-NLS-1$
+ }
+ // Arguments
+ pckState.writeDelim();
+ for (String capability : getCapabilitiesV2(capabilities)) {
+ pckState.writeString(capability);
+ }
+ if (!sendWants(want, pckState)) {
+ // We already have everything we wanted.
+ return;
+ }
+ // If we send something, we always close it properly ourselves.
+ outNeedsEnd = false;
+
+ FetchStateV2 fetchState = new FetchStateV2();
+ boolean sentDone = false;
+ for (;;) {
+ // The "state" buffer contains the full fetch request with all
+ // common objects found so far.
+ state.writeTo(out, monitor);
+ sentDone = sendNextHaveBatch(fetchState, pckOut, monitor);
+ if (sentDone) {
+ break;
+ }
+ if (readAcknowledgments(fetchState, pckIn, monitor)) {
+ // We got a "ready": next should be a patch file.
+ break;
+ }
+ // Note: C git reads and requires here (and after a packfile) a
+ // "0002" packet in stateless RPC transports (https). This "response
+ // end" packet is even mentioned in the protocol V2 technical
+ // documentation. However, it is not actually part of the public
+ // protocol; it occurs only in an internal protocol wrapper in the C
+ // git implementation.
+ }
+ clearState();
+ String line = pckIn.readString();
+ // If we sent a done, we may have an error reply here.
+ if (sentDone && line.startsWith("ERR ")) { //$NON-NLS-1$
+ throw new RemoteRepositoryException(uri, line.substring(4));
+ }
+ // "shallow-info", "wanted-refs", and "packfile-uris" would have to be
+ // handled here in that order.
+ if (!GitProtocolConstants.SECTION_PACKFILE.equals(line)) {
+ throw new PackProtocolException(
+ MessageFormat.format(JGitText.get().expectedGot,
+ GitProtocolConstants.SECTION_PACKFILE, line));
+ }
+ receivePack(monitor, outputStream);
+ }
+
+ /**
+ * Sends the next batch of "have"s and terminates the {@code output}.
+ *
+ * @param fetchState
+ * is updated with information about the number of items written,
+ * and whether to expect a packfile next
+ * @param output
+ * to write to
+ * @param monitor
+ * for progress reporting and cancellation
+ * @return {@code true} if a "done" was written and we should thus expect a
+ * packfile next
+ * @throws IOException
+ * on errors
+ * @throws CancelledException
+ * on cancellation
+ */
+ private boolean sendNextHaveBatch(FetchStateV2 fetchState,
+ PacketLineOut output, ProgressMonitor monitor)
+ throws IOException, CancelledException {
+ long n = 0;
+ while (n < fetchState.havesToSend) {
+ final RevCommit c = walk.next();
+ if (c == null) {
+ break;
+ }
+ output.writeString("have " + c.getId().name() + '\n'); //$NON-NLS-1$
+ n++;
+ if (n % 10 == 0 && monitor.isCancelled()) {
+ throw new CancelledException();
+ }
+ }
+ fetchState.havesTotal += n;
+ if (n == 0
+ || (fetchState.hadAcks
+ && fetchState.havesWithoutAck > MAX_HAVES)
+ || fetchState.havesTotal > maxHaves) {
+ output.writeString("done\n"); //$NON-NLS-1$
+ output.end();
+ return true;
+ }
+ // Increment only after the test above. Of course we have no ACKs yet
+ // for the newly added "have"s, so it makes no sense to count them
+ // against the MAX_HAVES limit.
+ fetchState.havesWithoutAck += n;
+ output.end();
+ fetchState.incHavesToSend(statelessRPC);
+ return false;
+ }
+
+ /**
+ * Reads and processes acknowledgments, adding ACKed objects as "have"s to
+ * the global state {@link TemporaryBuffer}.
+ *
+ * @param fetchState
+ * to update
+ * @param input
+ * to read from
+ * @param monitor
+ * for progress reporting and cancellation
+ * @return {@code true} if a "ready" was received and a packfile is expected
+ * next
+ * @throws IOException
+ * on errors
+ * @throws CancelledException
+ * on cancellation
+ */
+ private boolean readAcknowledgments(FetchStateV2 fetchState,
+ PacketLineIn input, ProgressMonitor monitor)
+ throws IOException, CancelledException {
+ String line = input.readString();
+ if (!GitProtocolConstants.SECTION_ACKNOWLEDGMENTS.equals(line)) {
+ throw new PackProtocolException(MessageFormat.format(
+ JGitText.get().expectedGot,
+ GitProtocolConstants.SECTION_ACKNOWLEDGMENTS, line));
+ }
+ MutableObjectId returnedId = new MutableObjectId();
+ line = input.readString();
+ boolean gotReady = false;
+ long n = 0;
+ while (!PacketLineIn.isEnd(line) && !PacketLineIn.isDelimiter(line)) {
+ AckNackResult ack = PacketLineIn.parseACKv2(line, returnedId);
+ // If we got a "ready", we just skip the remaining lines after
+ // having checked them for being valid. (Normally, the "ready"
+ // should be the last line anyway.)
+ if (!gotReady) {
+ if (ack == AckNackResult.ACK_COMMON) {
+ // markCommon appends the object to the "state"
+ markCommon(walk.parseAny(returnedId), ack, true);
+ fetchState.havesWithoutAck = 0;
+ fetchState.hadAcks = true;
+ } else if (ack == AckNackResult.ACK_READY) {
+ gotReady = true;
+ }
+ }
+ n++;
+ if (n % 10 == 0 && monitor.isCancelled()) {
+ throw new CancelledException();
+ }
+ line = input.readString();
+ }
+ if (gotReady) {
+ if (!PacketLineIn.isDelimiter(line)) {
+ throw new PackProtocolException(MessageFormat
+ .format(JGitText.get().expectedGot, "0001", line)); //$NON-NLS-1$
+ }
+ } else if (!PacketLineIn.isEnd(line)) {
+ throw new PackProtocolException(MessageFormat
+ .format(JGitText.get().expectedGot, "0000", line)); //$NON-NLS-1$
+ }
+ return gotReady;
+ }
+
/** {@inheritDoc} */
@Override
public void close() {
@@ -456,8 +665,8 @@ public abstract class BasePackFetchConnection extends BasePackConnection
}
}
- private boolean sendWants(Collection<Ref> want) throws IOException {
- final PacketLineOut p = statelessRPC ? pckState : pckOut;
+ private boolean sendWants(Collection<Ref> want, PacketLineOut p)
+ throws IOException {
boolean first = true;
for (Ref r : want) {
ObjectId objectId = r.getObjectId();
@@ -479,10 +688,11 @@ public abstract class BasePackFetchConnection extends BasePackConnection
final StringBuilder line = new StringBuilder(46);
line.append("want "); //$NON-NLS-1$
line.append(objectId.name());
- if (first) {
+ if (first && TransferConfig.ProtocolVersion.V0
+ .equals(getProtocolVersion())) {
line.append(enableCapabilities());
- first = false;
}
+ first = false;
line.append('\n');
p.writeString(line.toString());
}
@@ -492,11 +702,34 @@ public abstract class BasePackFetchConnection extends BasePackConnection
if (!filterSpec.isNoOp()) {
p.writeString(filterSpec.filterLine());
}
- p.end();
- outNeedsEnd = false;
return true;
}
+ private Set<String> getCapabilitiesV2(Set<String> advertisedCapabilities)
+ throws TransportException {
+ Set<String> capabilities = new LinkedHashSet<>();
+ // Protocol V2 is implicitly capable of all these.
+ if (noProgress) {
+ capabilities.add(OPTION_NO_PROGRESS);
+ }
+ if (includeTags) {
+ capabilities.add(OPTION_INCLUDE_TAG);
+ }
+ if (allowOfsDelta) {
+ capabilities.add(OPTION_OFS_DELTA);
+ }
+ if (thinPack) {
+ capabilities.add(OPTION_THIN_PACK);
+ }
+ if (!filterSpec.isNoOp()
+ && !advertisedCapabilities.contains(OPTION_FILTER)) {
+ throw new PackProtocolException(uri,
+ JGitText.get().filterRequiresCapability);
+ }
+ // The FilterSpec will be added later in sendWants().
+ return capabilities;
+ }
+
private String enableCapabilities() throws TransportException {
final StringBuilder line = new StringBuilder();
if (noProgress)
@@ -622,7 +855,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
// we need to continue to talk about other parts of
// our local history.
//
- markCommon(walk.parseAny(ackId), anr);
+ markCommon(walk.parseAny(ackId), anr, statelessRPC);
receivedAck = true;
receivedContinue = true;
havesSinceLastContinue = 0;
@@ -757,16 +990,10 @@ public abstract class BasePackFetchConnection extends BasePackConnection
}
}
- private void markCommon(RevObject obj, AckNackResult anr)
+ private void markCommon(RevObject obj, AckNackResult anr, boolean useState)
throws IOException {
- if (statelessRPC && anr == AckNackResult.ACK_COMMON && !obj.has(STATE)) {
- StringBuilder s;
-
- s = new StringBuilder(6 + Constants.OBJECT_ID_STRING_LENGTH);
- s.append("have "); //$NON-NLS-1$
- s.append(obj.name());
- s.append('\n');
- pckState.writeString(s.toString());
+ if (useState && anr == AckNackResult.ACK_COMMON && !obj.has(STATE)) {
+ pckState.writeString("have " + obj.name() + '\n'); //$NON-NLS-1$
obj.add(STATE);
}
obj.add(COMMON);
@@ -806,4 +1033,31 @@ public abstract class BasePackFetchConnection extends BasePackConnection
private static class CancelledException extends Exception {
private static final long serialVersionUID = 1L;
}
+
+ private static class FetchStateV2 {
+
+ long havesToSend = 32;
+
+ long havesTotal;
+
+ // Set to true if we got at least one ACK in protocol V2.
+ boolean hadAcks;
+
+ // Counts haves without ACK. Use as cutoff for negotiation only once
+ // hadAcks == true.
+ long havesWithoutAck;
+
+ void incHavesToSend(boolean statelessRPC) {
+ if (statelessRPC) {
+ // Increase this quicker since connection setup costs accumulate
+ if (havesToSend < 16384) {
+ havesToSend *= 2;
+ } else {
+ havesToSend = havesToSend * 11 / 10;
+ }
+ } else {
+ havesToSend += 32;
+ }
+ }
+ }
}
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 0f1892a97e..34bad6e029 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -48,6 +48,7 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.util.StringUtils;
class FetchProcess {
/** Transport we will fetch over. */
@@ -79,7 +80,8 @@ class FetchProcess {
toFetch = f;
}
- void execute(ProgressMonitor monitor, FetchResult result)
+ void execute(ProgressMonitor monitor, FetchResult result,
+ String initialBranch)
throws NotSupportedException, TransportException {
askFor.clear();
localUpdates.clear();
@@ -87,24 +89,64 @@ class FetchProcess {
packLocks.clear();
localRefs = null;
+ Throwable e1 = null;
try {
- executeImp(monitor, result);
+ executeImp(monitor, result, initialBranch);
+ } catch (NotSupportedException | TransportException err) {
+ e1 = err;
+ throw err;
} finally {
try {
- for (PackLock lock : packLocks)
- lock.unlock();
+ for (PackLock lock : packLocks) {
+ lock.unlock();
+ }
} catch (IOException e) {
+ if (e1 != null) {
+ e.addSuppressed(e1);
+ }
throw new TransportException(e.getMessage(), e);
}
}
}
+ private boolean isInitialBranchMissing(Map<String, Ref> refsMap,
+ String initialBranch) {
+ if (StringUtils.isEmptyOrNull(initialBranch) || refsMap.isEmpty()) {
+ return false;
+ }
+ if (refsMap.containsKey(initialBranch)
+ || refsMap.containsKey(Constants.R_HEADS + initialBranch)
+ || refsMap.containsKey(Constants.R_TAGS + initialBranch)) {
+ return false;
+ }
+ return true;
+ }
+
private void executeImp(final ProgressMonitor monitor,
- final FetchResult result) throws NotSupportedException,
- TransportException {
- conn = transport.openFetch();
+ final FetchResult result, String initialBranch)
+ throws NotSupportedException, TransportException {
+ final TagOpt tagopt = transport.getTagOpt();
+ String getTags = (tagopt == TagOpt.NO_TAGS) ? null : Constants.R_TAGS;
+ String getHead = null;
+ try {
+ // If we don't have a HEAD yet, we're cloning and need to get the
+ // upstream HEAD, too.
+ Ref head = transport.local.exactRef(Constants.HEAD);
+ ObjectId id = head != null ? head.getObjectId() : null;
+ if (id == null || id.equals(ObjectId.zeroId())) {
+ getHead = Constants.HEAD;
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ conn = transport.openFetch(toFetch, getTags, getHead);
try {
- result.setAdvertisedRefs(transport.getURI(), conn.getRefsMap());
+ Map<String, Ref> refsMap = conn.getRefsMap();
+ if (isInitialBranchMissing(refsMap, initialBranch)) {
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().remoteBranchNotFound, initialBranch));
+ }
+ result.setAdvertisedRefs(transport.getURI(), refsMap);
result.peerUserAgent = conn.getPeerUserAgent();
final Set<Ref> matched = new HashSet<>();
for (RefSpec spec : toFetch) {
@@ -119,7 +161,6 @@ class FetchProcess {
}
Collection<Ref> additionalTags = Collections.<Ref> emptyList();
- final TagOpt tagopt = transport.getTagOpt();
if (tagopt == TagOpt.AUTO_FOLLOW)
additionalTags = expandAutoFollowTags();
else if (tagopt == TagOpt.FETCH_TAGS)
@@ -253,7 +294,17 @@ class FetchProcess {
if (conn != null)
return;
- conn = transport.openFetch();
+ // Build prefixes
+ Set<String> prefixes = new HashSet<>();
+ for (Ref toGet : askFor.values()) {
+ String src = toGet.getName();
+ prefixes.add(src);
+ prefixes.add(Constants.R_REFS + src);
+ prefixes.add(Constants.R_HEADS + src);
+ prefixes.add(Constants.R_TAGS + src);
+ }
+ conn = transport.openFetch(Collections.emptyList(),
+ prefixes.toArray(new String[0]));
// Since we opened a new connection we cannot be certain
// that the system we connected to has the same exact set
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
index 35e2978bc4..36fce7a3f5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2008-2013, Google Inc.
+ * Copyright (C) 2008, 2013 Google Inc.
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -247,6 +247,74 @@ public final class GitProtocolConstants {
*/
public static final String COMMAND_FETCH = "fetch"; //$NON-NLS-1$
+ /**
+ * HTTP header to set by clients to request a specific git protocol version
+ * in the HTTP transport.
+ *
+ * @since 5.11
+ */
+ public static final String PROTOCOL_HEADER = "Git-Protocol"; //$NON-NLS-1$
+
+ /**
+ * Environment variable to set by clients to request a specific git protocol
+ * in the file:// and ssh:// transports.
+ *
+ * @since 5.11
+ */
+ public static final String PROTOCOL_ENVIRONMENT_VARIABLE = "GIT_PROTOCOL"; //$NON-NLS-1$
+
+ /**
+ * Protocol V2 ref advertisement attribute containing the peeled object id
+ * for annotated tags.
+ *
+ * @since 5.11
+ */
+ public static final String REF_ATTR_PEELED = "peeled:"; //$NON-NLS-1$
+
+ /**
+ * Protocol V2 ref advertisement attribute containing the name of the ref
+ * for symbolic refs.
+ *
+ * @since 5.11
+ */
+ public static final String REF_ATTR_SYMREF_TARGET = "symref-target:"; //$NON-NLS-1$
+
+ /**
+ * Protocol V2 acknowledgments section header.
+ *
+ * @since 5.11
+ */
+ public static final String SECTION_ACKNOWLEDGMENTS = "acknowledgments"; //$NON-NLS-1$
+
+ /**
+ * Protocol V2 packfile section header.
+ *
+ * @since 5.11
+ */
+ public static final String SECTION_PACKFILE = "packfile"; //$NON-NLS-1$
+
+ /**
+ * Protocol announcement for protocol version 1. This is the same as V0,
+ * except for this initial line.
+ *
+ * @since 5.11
+ */
+ public static final String VERSION_1 = "version 1"; //$NON-NLS-1$
+
+ /**
+ * Protocol announcement for protocol version 2.
+ *
+ * @since 5.11
+ */
+ public static final String VERSION_2 = "version 2"; //$NON-NLS-1$
+
+ /**
+ * Protocol request for protocol version 2.
+ *
+ * @since 5.11
+ */
+ public static final String VERSION_2_REQUEST = "version=2"; //$NON-NLS-1$
+
enum MultiAck {
OFF, CONTINUE, DETAILED;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpTransport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpTransport.java
index 49c8b587d7..febeb3ce9c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpTransport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpTransport.java
@@ -26,7 +26,7 @@ public abstract class HttpTransport extends Transport {
*
* @since 3.3
*/
- protected static HttpConnectionFactory connectionFactory = new JDKHttpConnectionFactory();
+ protected static volatile HttpConnectionFactory connectionFactory = new JDKHttpConnectionFactory();
/**
* Get the {@link org.eclipse.jgit.transport.http.HttpConnectionFactory}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
index 350311ecc8..68c5b348ad 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
@@ -103,6 +103,38 @@ public class PacketLineIn {
this.limit = limit;
}
+ /**
+ * Parses a ACK/NAK line in protocol V2.
+ *
+ * @param line
+ * to parse
+ * @param returnedId
+ * in case of {@link AckNackResult#ACK_COMMON ACK_COMMON}
+ * @return one of {@link AckNackResult#NAK NAK},
+ * {@link AckNackResult#ACK_COMMON ACK_COMMON}, or
+ * {@link AckNackResult#ACK_READY ACK_READY}
+ * @throws IOException
+ * on protocol or transport errors
+ */
+ static AckNackResult parseACKv2(String line, MutableObjectId returnedId)
+ throws IOException {
+ if ("NAK".equals(line)) { //$NON-NLS-1$
+ return AckNackResult.NAK;
+ }
+ if (line.startsWith("ACK ") && line.length() == 44) { //$NON-NLS-1$
+ returnedId.fromString(line.substring(4, 44));
+ return AckNackResult.ACK_COMMON;
+ }
+ if ("ready".equals(line)) { //$NON-NLS-1$
+ return AckNackResult.ACK_READY;
+ }
+ if (line.startsWith("ERR ")) { //$NON-NLS-1$
+ throw new PackProtocolException(line.substring(4));
+ }
+ throw new PackProtocolException(
+ MessageFormat.format(JGitText.get().expectedACKNAKGot, line));
+ }
+
AckNackResult readACK(MutableObjectId returnedId) throws IOException {
final String line = readString();
if (line.length() == 0)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java
index 6fc2042e1f..77f0a7a516 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2008-2010, Google Inc.
- * Copyright (C) 2008-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2010 Google Inc.
+ * Copyright (C) 2008, 2009 Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -33,12 +33,15 @@ import org.slf4j.LoggerFactory;
* against the underlying OutputStream.
*/
public class PacketLineOut {
+
private static final Logger log = LoggerFactory.getLogger(PacketLineOut.class);
private final OutputStream out;
private final byte[] lenbuffer;
+ private final boolean logEnabled;
+
private boolean flushOnEnd;
private boolean usingSideband;
@@ -50,9 +53,24 @@ public class PacketLineOut {
* stream.
*/
public PacketLineOut(OutputStream outputStream) {
+ this(outputStream, true);
+ }
+
+ /**
+ * Create a new packet line writer that potentially doesn't log.
+ *
+ * @param outputStream
+ * stream.
+ * @param enableLogging
+ * {@code false} to suppress all logging; {@code true} to log
+ * normally
+ * @since 5.11
+ */
+ public PacketLineOut(OutputStream outputStream, boolean enableLogging) {
out = outputStream;
lenbuffer = new byte[5];
flushOnEnd = true;
+ logEnabled = enableLogging;
}
/**
@@ -139,9 +157,15 @@ public class PacketLineOut {
out.write(lenbuffer, 0, 4);
}
out.write(buf, pos, len);
- if (log.isDebugEnabled()) {
- String s = RawParseUtils.decode(UTF_8, buf, pos, len);
- log.debug("git> " + s); //$NON-NLS-1$
+ if (logEnabled && log.isDebugEnabled()) {
+ // Escape a trailing \n to avoid empty lines in the log.
+ if (len > 0 && buf[pos + len - 1] == '\n') {
+ log.debug(
+ "git> " + RawParseUtils.decode(UTF_8, buf, pos, len - 1) //$NON-NLS-1$
+ + "\\n"); //$NON-NLS-1$
+ } else {
+ log.debug("git> " + RawParseUtils.decode(UTF_8, buf, pos, len)); //$NON-NLS-1$
+ }
}
}
@@ -156,7 +180,9 @@ public class PacketLineOut {
public void writeDelim() throws IOException {
formatLength(1);
out.write(lenbuffer, 0, 4);
- log.debug("git> 0001"); //$NON-NLS-1$
+ if (logEnabled && log.isDebugEnabled()) {
+ log.debug("git> 0001"); //$NON-NLS-1$
+ }
}
/**
@@ -175,9 +201,12 @@ public class PacketLineOut {
public void end() throws IOException {
formatLength(0);
out.write(lenbuffer, 0, 4);
- log.debug("git> 0000"); //$NON-NLS-1$
- if (flushOnEnd)
+ if (logEnabled && log.isDebugEnabled()) {
+ log.debug("git> 0000"); //$NON-NLS-1$
+ }
+ if (flushOnEnd) {
flush();
+ }
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
index 3adebba03c..c525e66848 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2010, Google Inc. and others
+ * Copyright (C) 2008, 2020 Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -13,6 +13,8 @@ package org.eclipse.jgit.transport;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SYMREF;
+import static org.eclipse.jgit.transport.GitProtocolConstants.REF_ATTR_PEELED;
+import static org.eclipse.jgit.transport.GitProtocolConstants.REF_ATTR_SYMREF_TARGET;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -287,7 +289,8 @@ public abstract class RefAdvertiser {
if (useProtocolV2) {
String symrefPart = symrefs.containsKey(ref.getName())
- ? (" symref-target:" + symrefs.get(ref.getName())) //$NON-NLS-1$
+ ? (' ' + REF_ATTR_SYMREF_TARGET
+ + symrefs.get(ref.getName()))
: ""; //$NON-NLS-1$
String peelPart = ""; //$NON-NLS-1$
if (derefTags) {
@@ -296,7 +299,8 @@ public abstract class RefAdvertiser {
}
ObjectId peeledObjectId = ref.getPeeledObjectId();
if (peeledObjectId != null) {
- peelPart = " peeled:" + peeledObjectId.getName(); //$NON-NLS-1$
+ peelPart = ' ' + REF_ATTR_PEELED
+ + peeledObjectId.getName();
}
}
writeOne(objectId.getName() + " " + ref.getName() + symrefPart //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession2.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession2.java
new file mode 100644
index 0000000000..23f670ae25
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession2.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.transport;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * A {@link RemoteSession} that supports passing environment variables to
+ * commands.
+ *
+ * @since 5.11
+ */
+public interface RemoteSession2 extends RemoteSession {
+
+ /**
+ * Creates a new remote {@link Process} to execute the given command. The
+ * returned process's streams exist and are connected, and execution of the
+ * process is already started.
+ *
+ * @param commandName
+ * command to execute
+ * @param environment
+ * environment variables to pass on
+ * @param timeout
+ * timeout value, in seconds, for creating the remote process
+ * @return a new remote process, already started
+ * @throws java.io.IOException
+ * may be thrown in several cases. For example, on problems
+ * opening input or output streams or on problems connecting or
+ * communicating with the remote host. For the latter two cases,
+ * a TransportException may be thrown (a subclass of
+ * java.io.IOException).
+ */
+ Process exec(String commandName, Map<String, String> environment,
+ int timeout) throws IOException;
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
index cc577fa11e..83ffd4123a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009, Google Inc. and others
+ * Copyright (C) 2008, 2020 Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -21,6 +21,7 @@ import java.util.Map;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.storage.file.LazyObjectIdSetFile;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Config.SectionParser;
import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.ObjectIdSet;
@@ -60,11 +61,19 @@ public class TransferConfig {
}
/**
- * A git configuration variable for which versions of the Git protocol to prefer.
- * Used in protocol.version.
+ * A git configuration variable for which versions of the Git protocol to
+ * prefer. Used in protocol.version.
+ *
+ * @since 5.9
*/
- enum ProtocolVersion {
+ public enum ProtocolVersion {
+ /**
+ * Git wire protocol version 0 (the default).
+ */
V0("0"), //$NON-NLS-1$
+ /**
+ * Git wire protocol version 2.
+ */
V2("2"); //$NON-NLS-1$
final String name;
@@ -73,6 +82,15 @@ public class TransferConfig {
this.name = name;
}
+ /**
+ * Returns version number
+ *
+ * @return string version
+ */
+ public String version() {
+ return name;
+ }
+
@Nullable
static ProtocolVersion parse(@Nullable String name) {
if (name == null) {
@@ -83,6 +101,9 @@ public class TransferConfig {
return v;
}
}
+ if ("1".equals(name)) { //$NON-NLS-1$
+ return V0;
+ }
return null;
}
}
@@ -177,7 +198,9 @@ public class TransferConfig {
"uploadpack", "allowreachablesha1inwant", false);
allowFilter = rc.getBoolean(
"uploadpack", "allowfilter", false);
- protocolVersion = ProtocolVersion.parse(rc.getString("protocol", null, "version"));
+ protocolVersion = ProtocolVersion.parse(rc
+ .getString(ConfigConstants.CONFIG_PROTOCOL_SECTION, null,
+ ConfigConstants.CONFIG_KEY_VERSION));
hideRefs = rc.getStringList("uploadpack", null, "hiderefs");
allowSidebandAll = rc.getBoolean(
"uploadpack", "allowsidebandall", false);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index 2ddd0a6128..5b781ac25f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -1,8 +1,8 @@
/*
- * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2008, 2009 Google Inc.
* Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -39,6 +39,7 @@ import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.AbortedByHookException;
import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.TransportException;
@@ -774,6 +775,10 @@ public abstract class Transport implements AutoCloseable {
private PrintStream hookOutRedirect;
private PrePushHook prePush;
+
+ @Nullable
+ TransferConfig.ProtocolVersion protocol;
+
/**
* Create a new transport instance.
*
@@ -789,6 +794,7 @@ public abstract class Transport implements AutoCloseable {
final TransferConfig tc = local.getConfig().get(TransferConfig.KEY);
this.local = local;
this.uri = uri;
+ this.protocol = tc.protocolVersion;
this.objectChecker = tc.newObjectChecker();
this.credentialsProvider = CredentialsProvider.getDefault();
prePush = Hooks.prePush(local, hookOutRedirect);
@@ -1225,9 +1231,52 @@ public abstract class Transport implements AutoCloseable {
* the remote connection could not be established or object
* copying (if necessary) failed or update specification was
* incorrect.
+ * @since 5.11
+ */
+ public FetchResult fetch(final ProgressMonitor monitor,
+ Collection<RefSpec> toFetch)
+ throws NotSupportedException, TransportException {
+ return fetch(monitor, toFetch, null);
+ }
+
+ /**
+ * Fetch objects and refs from the remote repository to the local one.
+ * <p>
+ * This is a utility function providing standard fetch behavior. Local
+ * tracking refs associated with the remote repository are automatically
+ * updated if this transport was created from a
+ * {@link org.eclipse.jgit.transport.RemoteConfig} with fetch RefSpecs
+ * defined.
+ *
+ * @param monitor
+ * progress monitor to inform the user about our processing
+ * activity. Must not be null. Use
+ * {@link org.eclipse.jgit.lib.NullProgressMonitor} if progress
+ * updates are not interesting or necessary.
+ * @param toFetch
+ * specification of refs to fetch locally. May be null or the
+ * empty collection to use the specifications from the
+ * RemoteConfig. Source for each RefSpec can't be null.
+ * @param branch
+ * the initial branch to check out when cloning the repository.
+ * Can be specified as ref name (<code>refs/heads/master</code>),
+ * branch name (<code>master</code>) or tag name
+ * (<code>v1.2.3</code>). The default is to use the branch
+ * pointed to by the cloned repository's HEAD and can be
+ * requested by passing {@code null} or <code>HEAD</code>.
+ * @return information describing the tracking refs updated.
+ * @throws org.eclipse.jgit.errors.NotSupportedException
+ * this transport implementation does not support fetching
+ * objects.
+ * @throws org.eclipse.jgit.errors.TransportException
+ * the remote connection could not be established or object
+ * copying (if necessary) failed or update specification was
+ * incorrect.
+ * @since 5.11
*/
public FetchResult fetch(final ProgressMonitor monitor,
- Collection<RefSpec> toFetch) throws NotSupportedException,
+ Collection<RefSpec> toFetch, String branch)
+ throws NotSupportedException,
TransportException {
if (toFetch == null || toFetch.isEmpty()) {
// If the caller did not ask for anything use the defaults.
@@ -1257,7 +1306,7 @@ public abstract class Transport implements AutoCloseable {
}
final FetchResult result = new FetchResult();
- new FetchProcess(this, toFetch).execute(monitor, result);
+ new FetchProcess(this, toFetch).execute(monitor, result, branch);
local.autoGC(monitor);
@@ -1453,6 +1502,43 @@ public abstract class Transport implements AutoCloseable {
TransportException;
/**
+ * Begins a new connection for fetching from the remote repository.
+ * <p>
+ * If the transport has no local repository, the fetch connection can only
+ * be used for reading remote refs.
+ * </p>
+ * <p>
+ * If the server supports git protocol V2, the {@link RefSpec}s and the
+ * additional patterns, if any, are used to restrict the server's ref
+ * advertisement to matching refs only.
+ * </p>
+ * <p>
+ * Transports that want to support git protocol V2 <em>must</em> override
+ * this; the default implementation ignores its arguments and calls
+ * {@link #openFetch()}.
+ * </p>
+ *
+ * @param refSpecs
+ * that will be fetched via
+ * {@link FetchConnection#fetch(ProgressMonitor, Collection, java.util.Set, OutputStream)} later
+ * @param additionalPatterns
+ * that will be set as ref prefixes if the server supports git
+ * protocol V2; {@code null} values are ignored
+ *
+ * @return a fresh connection to fetch from the remote repository.
+ * @throws org.eclipse.jgit.errors.NotSupportedException
+ * the implementation does not support fetching.
+ * @throws org.eclipse.jgit.errors.TransportException
+ * the remote connection could not be established.
+ * @since 5.11
+ */
+ public FetchConnection openFetch(Collection<RefSpec> refSpecs,
+ String... additionalPatterns)
+ throws NotSupportedException, TransportException {
+ return openFetch();
+ }
+
+ /**
* Begins a new connection for pushing into the remote repository.
*
* @return a fresh connection to push into the remote repository.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitAnon.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitAnon.java
index 820ec1a67a..a1914b6182 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitAnon.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitAnon.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -22,6 +22,7 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
+import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
@@ -94,6 +95,13 @@ class TransportGitAnon extends TcpTransport implements PackTransport {
return new TcpFetchConnection();
}
+ @Override
+ public FetchConnection openFetch(Collection<RefSpec> refSpecs,
+ String... additionalPatterns)
+ throws NotSupportedException, TransportException {
+ return new TcpFetchConnection(refSpecs, additionalPatterns);
+ }
+
/** {@inheritDoc} */
@Override
public PushConnection openPush() throws TransportException {
@@ -113,7 +121,6 @@ class TransportGitAnon extends TcpTransport implements PackTransport {
final Socket s = new Socket();
try {
final InetAddress host = InetAddress.getByName(uri.getHost());
- s.bind(null);
s.connect(new InetSocketAddress(host, port), tms);
} catch (IOException c) {
try {
@@ -130,7 +137,8 @@ class TransportGitAnon extends TcpTransport implements PackTransport {
return s;
}
- void service(String name, PacketLineOut pckOut)
+ void service(String name, PacketLineOut pckOut,
+ TransferConfig.ProtocolVersion gitProtocol)
throws IOException {
final StringBuilder cmd = new StringBuilder();
cmd.append(name);
@@ -144,6 +152,11 @@ class TransportGitAnon extends TcpTransport implements PackTransport {
cmd.append(uri.getPort());
}
cmd.append('\0');
+ if (TransferConfig.ProtocolVersion.V2.equals(gitProtocol)) {
+ cmd.append('\0');
+ cmd.append(GitProtocolConstants.VERSION_2_REQUEST);
+ cmd.append('\0');
+ }
pckOut.writeString(cmd.toString());
pckOut.flush();
}
@@ -152,6 +165,11 @@ class TransportGitAnon extends TcpTransport implements PackTransport {
private Socket sock;
TcpFetchConnection() throws TransportException {
+ this(Collections.emptyList());
+ }
+
+ TcpFetchConnection(Collection<RefSpec> refSpecs,
+ String... additionalPatterns) throws TransportException {
super(TransportGitAnon.this);
sock = openConnection();
try {
@@ -162,13 +180,19 @@ class TransportGitAnon extends TcpTransport implements PackTransport {
sOut = new BufferedOutputStream(sOut);
init(sIn, sOut);
- service("git-upload-pack", pckOut); //$NON-NLS-1$
+ TransferConfig.ProtocolVersion gitProtocol = protocol;
+ if (gitProtocol == null) {
+ gitProtocol = TransferConfig.ProtocolVersion.V2;
+ }
+ service("git-upload-pack", pckOut, gitProtocol); //$NON-NLS-1$
} catch (IOException err) {
close();
throw new TransportException(uri,
JGitText.get().remoteHungUpUnexpectedly, err);
}
- readAdvertisedRefs();
+ if (!readAdvertisedRefs()) {
+ lsRefs(refSpecs, additionalPatterns);
+ }
}
@Override
@@ -201,7 +225,7 @@ class TransportGitAnon extends TcpTransport implements PackTransport {
sOut = new BufferedOutputStream(sOut);
init(sIn, sOut);
- service("git-receive-pack", pckOut); //$NON-NLS-1$
+ service("git-receive-pack", pckOut, null); //$NON-NLS-1$
} catch (IOException err) {
close();
throw new TransportException(uri,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
index b9cb2484d8..19ed4fbcc1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
@@ -1,8 +1,8 @@
/*
- * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2008, 2010 Google Inc.
* Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -19,11 +19,13 @@ import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.errors.NoRemoteRepositoryException;
@@ -144,6 +146,13 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
return new SshFetchConnection();
}
+ @Override
+ public FetchConnection openFetch(Collection<RefSpec> refSpecs,
+ String... additionalPatterns)
+ throws NotSupportedException, TransportException {
+ return new SshFetchConnection(refSpecs, additionalPatterns);
+ }
+
/** {@inheritDoc} */
@Override
public PushConnection openPush() throws TransportException {
@@ -196,29 +205,38 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
return SystemReader.getInstance().getenv("GIT_SSH") != null; //$NON-NLS-1$
}
- private class ExtSession implements RemoteSession {
+ private class ExtSession implements RemoteSession2 {
+
@Override
public Process exec(String command, int timeout)
throws TransportException {
+ return exec(command, null, timeout);
+ }
+
+ @Override
+ public Process exec(String command, Map<String, String> environment,
+ int timeout) throws TransportException {
String ssh = SystemReader.getInstance().getenv("GIT_SSH"); //$NON-NLS-1$
boolean putty = ssh.toLowerCase(Locale.ROOT).contains("plink"); //$NON-NLS-1$
List<String> args = new ArrayList<>();
args.add(ssh);
- if (putty
- && !ssh.toLowerCase(Locale.ROOT).contains("tortoiseplink")) //$NON-NLS-1$
+ if (putty && !ssh.toLowerCase(Locale.ROOT)
+ .contains("tortoiseplink")) {//$NON-NLS-1$
args.add("-batch"); //$NON-NLS-1$
+ }
if (0 < getURI().getPort()) {
args.add(putty ? "-P" : "-p"); //$NON-NLS-1$ //$NON-NLS-2$
args.add(String.valueOf(getURI().getPort()));
}
- if (getURI().getUser() != null)
+ if (getURI().getUser() != null) {
args.add(getURI().getUser() + "@" + getURI().getHost()); //$NON-NLS-1$
- else
+ } else {
args.add(getURI().getHost());
+ }
args.add(command);
- ProcessBuilder pb = createProcess(args);
+ ProcessBuilder pb = createProcess(args, environment);
try {
return pb.start();
} catch (IOException err) {
@@ -226,9 +244,13 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
}
}
- private ProcessBuilder createProcess(List<String> args) {
+ private ProcessBuilder createProcess(List<String> args,
+ Map<String, String> environment) {
ProcessBuilder pb = new ProcessBuilder();
pb.command(args);
+ if (environment != null) {
+ pb.environment().putAll(environment);
+ }
File directory = local != null ? local.getDirectory() : null;
if (directory != null) {
pb.environment().put(Constants.GIT_DIR_KEY,
@@ -249,10 +271,31 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
private StreamCopyThread errorThread;
SshFetchConnection() throws TransportException {
+ this(Collections.emptyList());
+ }
+
+ SshFetchConnection(Collection<RefSpec> refSpecs,
+ String... additionalPatterns) throws TransportException {
super(TransportGitSsh.this);
try {
- process = getSession().exec(commandFor(getOptionUploadPack()),
- getTimeout());
+ RemoteSession session = getSession();
+ TransferConfig.ProtocolVersion gitProtocol = protocol;
+ if (gitProtocol == null) {
+ gitProtocol = TransferConfig.ProtocolVersion.V2;
+ }
+ if (session instanceof RemoteSession2
+ && TransferConfig.ProtocolVersion.V2
+ .equals(gitProtocol)) {
+ process = ((RemoteSession2) session).exec(
+ commandFor(getOptionUploadPack()), Collections
+ .singletonMap(
+ GitProtocolConstants.PROTOCOL_ENVIRONMENT_VARIABLE,
+ GitProtocolConstants.VERSION_2_REQUEST),
+ getTimeout());
+ } else {
+ process = session.exec(commandFor(getOptionUploadPack()),
+ getTimeout());
+ }
final MessageWriter msg = new MessageWriter();
setMessageWriter(msg);
@@ -272,7 +315,9 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
}
try {
- readAdvertisedRefs();
+ if (!readAdvertisedRefs()) {
+ lsRefs(refSpecs, additionalPatterns);
+ }
} catch (NoRemoteRepositoryException notFound) {
final String msgs = getMessages();
checkExecFailure(process.exitValue(), getOptionUploadPack(),
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
index 6768387e65..2e5d18dc15 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -1,8 +1,8 @@
/*
- * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2008, 2010 Google Inc.
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
* Copyright (C) 2013, Matthias Sohn <matthias.sohn@sap.com>
- * Copyright (C) 2017, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2017, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -33,14 +33,15 @@ import static org.eclipse.jgit.util.HttpSupport.HDR_WWW_AUTHENTICATE;
import static org.eclipse.jgit.util.HttpSupport.METHOD_GET;
import static org.eclipse.jgit.util.HttpSupport.METHOD_POST;
+import java.io.BufferedInputStream;
import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
import java.net.HttpCookie;
import java.net.MalformedURLException;
import java.net.Proxy;
@@ -49,10 +50,12 @@ import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
+import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException;
@@ -75,6 +78,7 @@ import java.util.zip.GZIPOutputStream;
import javax.net.ssl.SSLHandshakeException;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.NoRemoteRepositoryException;
import org.eclipse.jgit.errors.NotSupportedException;
@@ -95,6 +99,8 @@ import org.eclipse.jgit.lib.SymbolicRef;
import org.eclipse.jgit.transport.HttpAuthMethod.Type;
import org.eclipse.jgit.transport.HttpConfig.HttpRedirectMode;
import org.eclipse.jgit.transport.http.HttpConnection;
+import org.eclipse.jgit.transport.http.HttpConnectionFactory;
+import org.eclipse.jgit.transport.http.HttpConnectionFactory2;
import org.eclipse.jgit.util.HttpSupport;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
@@ -132,6 +138,9 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
private static final String SVC_RECEIVE_PACK = "git-receive-pack"; //$NON-NLS-1$
+ private static final byte[] VERSION = "version" //$NON-NLS-1$
+ .getBytes(StandardCharsets.US_ASCII);
+
/**
* Accept-Encoding header in the HTTP request
* (https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html).
@@ -257,6 +266,12 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
private boolean sslFailure = false;
+ private HttpConnectionFactory factory;
+
+ private HttpConnectionFactory2.GitSession gitSession;
+
+ private boolean factoryUsed;
+
/**
* All stored cookies bound to this repo (independent of the baseUrl)
*/
@@ -279,6 +294,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
sslVerify = http.isSslVerify();
cookieFile = getCookieFileFromConfig(http);
relevantCookies = filterCookies(cookieFile, baseUrl);
+ factory = HttpTransport.getConnectionFactory();
}
private URL toURL(URIish urish) throws MalformedURLException {
@@ -321,6 +337,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
sslVerify = http.isSslVerify();
cookieFile = getCookieFileFromConfig(http);
relevantCookies = filterCookies(cookieFile, baseUrl);
+ factory = HttpTransport.getConnectionFactory();
}
/**
@@ -339,11 +356,15 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
@SuppressWarnings("resource") // Closed by caller
private FetchConnection getConnection(HttpConnection c, InputStream in,
- String service) throws IOException {
+ String service, Collection<RefSpec> refSpecs,
+ String... additionalPatterns) throws IOException {
BaseConnection f;
if (isSmartHttp(c, service)) {
- readSmartHeaders(in, service);
- f = new SmartHttpFetchConnection(in);
+ InputStream withMark = in.markSupported() ? in
+ : new BufferedInputStream(in);
+ readSmartHeaders(withMark, service);
+ f = new SmartHttpFetchConnection(withMark, refSpecs,
+ additionalPatterns);
} else {
// Assume this server doesn't support smart HTTP fetch
// and fall back on dumb object walking.
@@ -353,15 +374,98 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
return (FetchConnection) f;
}
+ /**
+ * Sets the {@link HttpConnectionFactory} to be used by this
+ * {@link TransportHttp} instance.
+ * <p>
+ * If no factory is set explicitly, the {@link TransportHttp} instance uses
+ * the {@link HttpTransport#getConnectionFactory() globally defined
+ * factory}.
+ * </p>
+ *
+ * @param customFactory
+ * the {@link HttpConnectionFactory} to use
+ * @throws IllegalStateException
+ * if an HTTP/HTTPS connection has already been opened on this
+ * {@link TransportHttp} instance
+ * @since 5.11
+ */
+ public void setHttpConnectionFactory(
+ @NonNull HttpConnectionFactory customFactory) {
+ if (factoryUsed) {
+ throw new IllegalStateException(JGitText.get().httpFactoryInUse);
+ }
+ factory = customFactory;
+ }
+
+ /**
+ * Retrieves the {@link HttpConnectionFactory} used by this
+ * {@link TransportHttp} instance.
+ *
+ * @return the {@link HttpConnectionFactory}
+ * @since 5.11
+ */
+ @NonNull
+ public HttpConnectionFactory getHttpConnectionFactory() {
+ return factory;
+ }
+
+ /**
+ * Sets preemptive Basic HTTP authentication. If the given {@code username}
+ * or {@code password} is empty or {@code null}, no preemptive
+ * authentication will be done. If {@code username} and {@code password} are
+ * set, they will override authority information from the URI
+ * ("user:password@").
+ * <p>
+ * If the connection encounters redirects, the pre-authentication will be
+ * cleared if the redirect goes to a different host.
+ * </p>
+ *
+ * @param username
+ * to use
+ * @param password
+ * to use
+ * @throws IllegalStateException
+ * if an HTTP/HTTPS connection has already been opened on this
+ * {@link TransportHttp} instance
+ * @since 5.11
+ */
+ public void setPreemptiveBasicAuthentication(String username,
+ String password) {
+ if (factoryUsed) {
+ throw new IllegalStateException(JGitText.get().httpPreAuthTooLate);
+ }
+ if (StringUtils.isEmptyOrNull(username)
+ || StringUtils.isEmptyOrNull(password)) {
+ authMethod = authFromUri(currentUri);
+ } else {
+ HttpAuthMethod basic = HttpAuthMethod.Type.BASIC.method(null);
+ basic.authorize(username, password);
+ authMethod = basic;
+ }
+ }
+
/** {@inheritDoc} */
@Override
public FetchConnection openFetch() throws TransportException,
NotSupportedException {
+ return openFetch(Collections.emptyList());
+ }
+
+ @Override
+ public FetchConnection openFetch(Collection<RefSpec> refSpecs,
+ String... additionalPatterns)
+ throws NotSupportedException, TransportException {
final String service = SVC_UPLOAD_PACK;
try {
- final HttpConnection c = connect(service);
+ TransferConfig.ProtocolVersion gitProtocol = protocol;
+ if (gitProtocol == null) {
+ gitProtocol = TransferConfig.ProtocolVersion.V2;
+ }
+ HttpConnection c = connect(service, gitProtocol);
try (InputStream in = openInputStream(c)) {
- return getConnection(c, in, service);
+ return getConnection(c, in, service, refSpecs,
+ additionalPatterns);
}
} catch (NotSupportedException | TransportException err) {
throw err;
@@ -456,8 +560,9 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
private PushConnection smartPush(String service, HttpConnection c,
InputStream in) throws IOException, TransportException {
- readSmartHeaders(in, service);
- SmartHttpPushConnection p = new SmartHttpPushConnection(in);
+ BufferedInputStream inBuf = new BufferedInputStream(in);
+ readSmartHeaders(inBuf, service);
+ SmartHttpPushConnection p = new SmartHttpPushConnection(inBuf);
p.setPeerUserAgent(c.getHeaderField(HttpSupport.HDR_SERVER));
return p;
}
@@ -465,7 +570,10 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
/** {@inheritDoc} */
@Override
public void close() {
- // No explicit connections are maintained.
+ if (gitSession != null) {
+ gitSession.close();
+ gitSession = null;
+ }
}
/**
@@ -492,9 +600,40 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
return new NoRemoteRepositoryException(u, text);
}
+ private HttpAuthMethod authFromUri(URIish u) {
+ String user = u.getUser();
+ String pass = u.getPass();
+ if (user != null && pass != null) {
+ try {
+ // User/password are _not_ application/x-www-form-urlencoded. In
+ // particular the "+" sign would be replaced by a space.
+ user = URLDecoder.decode(user.replace("+", "%2B"), //$NON-NLS-1$ //$NON-NLS-2$
+ StandardCharsets.UTF_8.name());
+ pass = URLDecoder.decode(pass.replace("+", "%2B"), //$NON-NLS-1$ //$NON-NLS-2$
+ StandardCharsets.UTF_8.name());
+ HttpAuthMethod basic = HttpAuthMethod.Type.BASIC.method(null);
+ basic.authorize(user, pass);
+ return basic;
+ } catch (IllegalArgumentException
+ | UnsupportedEncodingException e) {
+ LOG.warn(JGitText.get().httpUserInfoDecodeError, u);
+ }
+ }
+ return HttpAuthMethod.Type.NONE.method(null);
+ }
+
private HttpConnection connect(String service)
throws TransportException, NotSupportedException {
+ return connect(service, null);
+ }
+
+ private HttpConnection connect(String service,
+ TransferConfig.ProtocolVersion protocolVersion)
+ throws TransportException, NotSupportedException {
URL u = getServiceURL(service);
+ if (HttpAuthMethod.Type.NONE.equals(authMethod.getType())) {
+ authMethod = authFromUri(currentUri);
+ }
int authAttempts = 1;
int redirects = 0;
Collection<Type> ignoreTypes = null;
@@ -507,6 +646,11 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
} else {
conn.setRequestProperty(HDR_ACCEPT, "*/*"); //$NON-NLS-1$
}
+ if (TransferConfig.ProtocolVersion.V2.equals(protocolVersion)) {
+ conn.setRequestProperty(
+ GitProtocolConstants.PROTOCOL_HEADER,
+ GitProtocolConstants.VERSION_2_REQUEST);
+ }
final int status = HttpSupport.response(conn);
processResponseCookies(conn);
switch (status) {
@@ -796,7 +940,13 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
}
try {
URI redirectTo = new URI(location);
+ // Reset authentication if the redirect has user/password info or
+ // if the host is different.
+ boolean resetAuth = !StringUtils
+ .isEmptyOrNull(redirectTo.getUserInfo());
+ String currentHost = currentUrl.getHost();
redirectTo = currentUrl.toURI().resolve(redirectTo);
+ resetAuth = resetAuth || !currentHost.equals(redirectTo.getHost());
String redirected = redirectTo.toASCIIString();
if (!isValidRedirect(baseUrl, redirected, checkFor)) {
throw new TransportException(uri,
@@ -805,6 +955,9 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
}
redirected = redirected.substring(0, redirected.indexOf(checkFor));
URIish result = new URIish(redirected);
+ if (resetAuth) {
+ authMethod = HttpAuthMethod.Type.NONE.method(null);
+ }
if (LOG.isInfoEnabled()) {
LOG.info(MessageFormat.format(JGitText.get().redirectHttp,
uri.setPass(null),
@@ -885,9 +1038,20 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
}
final Proxy proxy = HttpSupport.proxyFor(proxySelector, u);
- HttpConnection conn = connectionFactory.create(u, proxy);
+ factoryUsed = true;
+ HttpConnection conn = factory.create(u, proxy);
- if (!sslVerify && "https".equals(u.getProtocol())) { //$NON-NLS-1$
+ if (gitSession == null && (factory instanceof HttpConnectionFactory2)) {
+ gitSession = ((HttpConnectionFactory2) factory).newSession();
+ }
+ if (gitSession != null) {
+ try {
+ gitSession.configure(conn, sslVerify);
+ } catch (GeneralSecurityException e) {
+ throw new IOException(e.getMessage(), e);
+ }
+ } else if (!sslVerify && "https".equals(u.getProtocol())) { //$NON-NLS-1$
+ // Backwards compatibility
HttpSupport.disableSslVerify(conn);
}
@@ -1148,20 +1312,37 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
private void readSmartHeaders(InputStream in, String service)
throws IOException {
- // A smart reply will have a '#' after the first 4 bytes, but
- // a dumb reply cannot contain a '#' until after byte 41. Do a
+ // A smart protocol V0 reply will have a '#' after the first 4 bytes,
+ // but a dumb reply cannot contain a '#' until after byte 41. Do a
// quick check to make sure its a smart reply before we parse
// as a pkt-line stream.
//
- final byte[] magic = new byte[5];
+ // There appears to be a confusion about this in protocol V2. Github
+ // sends the # service line as a git (not http) header also when
+ // protocol V2 is used. Gitlab also does so. JGit's UploadPack doesn't,
+ // and thus Gerrit also does not.
+ final byte[] magic = new byte[14];
+ if (!in.markSupported()) {
+ throw new TransportException(uri,
+ JGitText.get().inputStreamMustSupportMark);
+ }
+ in.mark(14);
IO.readFully(in, magic, 0, magic.length);
+ // Did we get 000dversion 2 or similar? (Canonical is 000eversion 2\n,
+ // but JGit and thus Gerrit omits the \n.)
+ if (Arrays.equals(Arrays.copyOfRange(magic, 4, 11), VERSION)
+ && magic[12] >= '1' && magic[12] <= '9') {
+ // It's a smart server doing version 1 or greater, but not sending
+ // the # service line header. Don't consume the version line.
+ in.reset();
+ return;
+ }
if (magic[4] != '#') {
throw new TransportException(uri, MessageFormat.format(
JGitText.get().expectedPktLineWithService, RawParseUtils.decode(magic)));
}
-
- final PacketLineIn pckIn = new PacketLineIn(new UnionInputStream(
- new ByteArrayInputStream(magic), in));
+ in.reset();
+ final PacketLineIn pckIn = new PacketLineIn(in);
final String exp = "# service=" + service; //$NON-NLS-1$
final String act = pckIn.readString();
if (!exp.equals(act)) {
@@ -1327,12 +1508,24 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
SmartHttpFetchConnection(InputStream advertisement)
throws TransportException {
+ this(advertisement, Collections.emptyList());
+ }
+
+ SmartHttpFetchConnection(InputStream advertisement,
+ Collection<RefSpec> refSpecs, String... additionalPatterns)
+ throws TransportException {
super(TransportHttp.this);
statelessRPC = true;
init(advertisement, DisabledOutputStream.INSTANCE);
outNeedsEnd = false;
- readAdvertisedRefs();
+ if (!readAdvertisedRefs()) {
+ // Must be protocol V2
+ LongPollService service = new LongPollService(SVC_UPLOAD_PACK,
+ getProtocolVersion());
+ init(service.getInputStream(), service.getOutputStream());
+ lsRefs(refSpecs, additionalPatterns);
+ }
}
@Override
@@ -1340,7 +1533,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
final Collection<Ref> want, final Set<ObjectId> have,
final OutputStream outputStream) throws TransportException {
try {
- svc = new MultiRequestService(SVC_UPLOAD_PACK);
+ svc = new MultiRequestService(SVC_UPLOAD_PACK,
+ getProtocolVersion());
init(svc.getInputStream(), svc.getOutputStream());
super.doFetch(monitor, want, have, outputStream);
} finally {
@@ -1369,7 +1563,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
protected void doPush(final ProgressMonitor monitor,
final Map<String, RemoteRefUpdate> refUpdates,
OutputStream outputStream) throws TransportException {
- final Service svc = new MultiRequestService(SVC_RECEIVE_PACK);
+ final Service svc = new MultiRequestService(SVC_RECEIVE_PACK,
+ getProtocolVersion());
init(svc.getInputStream(), svc.getOutputStream());
super.doPush(monitor, refUpdates, outputStream);
}
@@ -1389,10 +1584,14 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
protected final HttpExecuteStream execute;
+ protected final TransferConfig.ProtocolVersion protocolVersion;
+
final UnionInputStream in;
- Service(String serviceName) {
+ Service(String serviceName,
+ TransferConfig.ProtocolVersion protocolVersion) {
this.serviceName = serviceName;
+ this.protocolVersion = protocolVersion;
this.requestType = "application/x-" + serviceName + "-request"; //$NON-NLS-1$ //$NON-NLS-2$
this.responseType = "application/x-" + serviceName + "-result"; //$NON-NLS-1$ //$NON-NLS-2$
@@ -1408,6 +1607,10 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
conn.setDoOutput(true);
conn.setRequestProperty(HDR_CONTENT_TYPE, requestType);
conn.setRequestProperty(HDR_ACCEPT, responseType);
+ if (TransferConfig.ProtocolVersion.V2.equals(protocolVersion)) {
+ conn.setRequestProperty(GitProtocolConstants.PROTOCOL_HEADER,
+ GitProtocolConstants.VERSION_2_REQUEST);
+ }
}
void sendRequest() throws IOException {
@@ -1663,8 +1866,9 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
class MultiRequestService extends Service {
boolean finalRequest;
- MultiRequestService(String serviceName) {
- super(serviceName);
+ MultiRequestService(String serviceName,
+ TransferConfig.ProtocolVersion protocolVersion) {
+ super(serviceName, protocolVersion);
}
/** Keep opening send-receive pairs to the given URI. */
@@ -1701,11 +1905,10 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
/** Service for maintaining a single long-poll connection. */
class LongPollService extends Service {
- /**
- * @param serviceName
- */
- LongPollService(String serviceName) {
- super(serviceName);
+
+ LongPollService(String serviceName,
+ TransferConfig.ProtocolVersion protocolVersion) {
+ super(serviceName, protocolVersion);
}
/** Only open one send-receive request. */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
index 403f98d869..77d1419ea2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
@@ -1,9 +1,9 @@
/*
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
- * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2008, 2010 Google Inc.
* Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -20,6 +20,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -153,11 +154,17 @@ class TransportLocal extends Transport implements PackTransport {
/** {@inheritDoc} */
@Override
public FetchConnection openFetch() throws TransportException {
+ return openFetch(Collections.emptyList());
+ }
+
+ @Override
+ public FetchConnection openFetch(Collection<RefSpec> refSpecs,
+ String... additionalPatterns) throws TransportException {
final String up = getOptionUploadPack();
if (!"git-upload-pack".equals(up) //$NON-NLS-1$
- && !"git upload-pack".equals(up)) //$NON-NLS-1$
- return new ForkLocalFetchConnection();
-
+ && !"git upload-pack".equals(up)) {//$NON-NLS-1$
+ return new ForkLocalFetchConnection(refSpecs, additionalPatterns);
+ }
UploadPackFactory<Void> upf = (Void req,
Repository db) -> createUploadPack(db);
return new InternalFetchConnection<>(this, upf, null, openRepo());
@@ -193,6 +200,23 @@ class TransportLocal extends Transport implements PackTransport {
*/
protected Process spawn(String cmd)
throws TransportException {
+ return spawn(cmd, null);
+ }
+
+ /**
+ * Spawn process
+ *
+ * @param cmd
+ * command
+ * @param protocolVersion
+ * to use
+ * @return a {@link java.lang.Process} object.
+ * @throws org.eclipse.jgit.errors.TransportException
+ * if any.
+ */
+ private Process spawn(String cmd,
+ TransferConfig.ProtocolVersion protocolVersion)
+ throws TransportException {
try {
String[] args = { "." }; //$NON-NLS-1$
ProcessBuilder proc = local.getFS().runInShell(cmd, args);
@@ -208,7 +232,10 @@ class TransportLocal extends Transport implements PackTransport {
env.remove("GIT_GRAFT_FILE"); //$NON-NLS-1$
env.remove("GIT_INDEX_FILE"); //$NON-NLS-1$
env.remove("GIT_NO_REPLACE_OBJECTS"); //$NON-NLS-1$
-
+ if (TransferConfig.ProtocolVersion.V2.equals(protocolVersion)) {
+ env.put(GitProtocolConstants.PROTOCOL_ENVIRONMENT_VARIABLE,
+ GitProtocolConstants.VERSION_2_REQUEST);
+ }
return proc.start();
} catch (IOException err) {
throw new TransportException(uri, err.getMessage(), err);
@@ -221,12 +248,21 @@ class TransportLocal extends Transport implements PackTransport {
private Thread errorReaderThread;
ForkLocalFetchConnection() throws TransportException {
+ this(Collections.emptyList());
+ }
+
+ ForkLocalFetchConnection(Collection<RefSpec> refSpecs,
+ String... additionalPatterns) throws TransportException {
super(TransportLocal.this);
final MessageWriter msg = new MessageWriter();
setMessageWriter(msg);
- uploadPack = spawn(getOptionUploadPack());
+ TransferConfig.ProtocolVersion gitProtocol = protocol;
+ if (gitProtocol == null) {
+ gitProtocol = TransferConfig.ProtocolVersion.V2;
+ }
+ uploadPack = spawn(getOptionUploadPack(), gitProtocol);
final InputStream upErr = uploadPack.getErrorStream();
errorReaderThread = new StreamCopyThread(upErr, msg.getRawStream());
@@ -239,7 +275,9 @@ class TransportLocal extends Transport implements PackTransport {
upOut = new BufferedOutputStream(upOut);
init(upIn, upOut);
- readAdvertisedRefs();
+ if (!readAdvertisedRefs()) {
+ lsRefs(refSpecs, additionalPatterns);
+ }
}
@Override
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 1242ef1b4a..7f1ddaab2e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2010, Google Inc. and others
+ * Copyright (C) 2008, 2020 Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -33,6 +33,7 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_AL
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
+import static org.eclipse.jgit.transport.GitProtocolConstants.VERSION_2_REQUEST;
import static org.eclipse.jgit.util.RefMap.toRefMap;
import java.io.ByteArrayOutputStream;
@@ -709,7 +710,7 @@ public class UploadPack {
* @since 5.0
*/
public void setExtraParameters(Collection<String> params) {
- this.clientRequestedV2 = params.contains("version=2"); //$NON-NLS-1$
+ this.clientRequestedV2 = params.contains(VERSION_2_REQUEST);
}
/**
@@ -722,7 +723,8 @@ public class UploadPack {
}
private boolean useProtocolV2() {
- return ProtocolVersion.V2.equals(transferConfig.protocolVersion)
+ return (transferConfig.protocolVersion == null
+ || ProtocolVersion.V2.equals(transferConfig.protocolVersion))
&& clientRequestedV2;
}
@@ -1191,17 +1193,18 @@ public class UploadPack {
if (req.wasDoneReceived()) {
processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
- new PacketLineOut(NullOutputStream.INSTANCE),
+ new PacketLineOut(NullOutputStream.INSTANCE, false),
accumulator);
} else {
- pckOut.writeString("acknowledgments\n"); //$NON-NLS-1$
+ pckOut.writeString(
+ GitProtocolConstants.SECTION_ACKNOWLEDGMENTS + '\n');
for (ObjectId id : req.getPeerHas()) {
if (walk.getObjectReader().has(id)) {
pckOut.writeString("ACK " + id.getName() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
- new PacketLineOut(NullOutputStream.INSTANCE),
+ new PacketLineOut(NullOutputStream.INSTANCE, false),
accumulator);
if (okToGiveUp()) {
pckOut.writeString("ready\n"); //$NON-NLS-1$
@@ -1243,7 +1246,8 @@ public class UploadPack {
if (!pckOut.isUsingSideband()) {
// sendPack will write "packfile\n" for us if sideband-all is used.
// But sideband-all is not used, so we have to write it ourselves.
- pckOut.writeString("packfile\n"); //$NON-NLS-1$
+ pckOut.writeString(
+ GitProtocolConstants.SECTION_PACKFILE + '\n');
}
accumulator.timeNegotiating = Duration
@@ -1955,8 +1959,8 @@ public class UploadPack {
.map(objId -> objectIdToRevObject(objWalk, objId))
.filter(Objects::nonNull); // Ignore missing tips
- ObjectReachabilityChecker reachabilityChecker = objWalk
- .createObjectReachabilityChecker();
+ ObjectReachabilityChecker reachabilityChecker = reader
+ .createObjectReachabilityChecker(objWalk);
Optional<RevObject> unreachable = reachabilityChecker
.areAllReachable(wantsAsObjs, startersAsObjs);
if (unreachable.isPresent()) {
@@ -1967,8 +1971,8 @@ public class UploadPack {
}
// All wants are commits, we can use ReachabilityChecker
- ReachabilityChecker reachabilityChecker = walk
- .createReachabilityChecker();
+ ReachabilityChecker reachabilityChecker = reader
+ .createReachabilityChecker(walk);
Stream<RevCommit> reachableCommits = importantRefsFirst(visibleRefs)
.map(UploadPack::refToObjectId)
@@ -2327,7 +2331,8 @@ public class UploadPack {
// for us if provided a PackfileUriConfig. In this case, we
// are not providing a PackfileUriConfig, so we have to
// write this line ourselves.
- pckOut.writeString("packfile\n"); //$NON-NLS-1$
+ pckOut.writeString(
+ GitProtocolConstants.SECTION_PACKFILE + '\n');
}
}
pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);
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 c44838ab89..a6b20451dd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
@@ -510,6 +510,7 @@ class WalkFetchConnection extends BaseFetchConnection {
// and attach it to the local repository so we can use
// all of the contained objects.
//
+ Throwable e1 = null;
try {
pack.downloadPack(monitor);
} catch (IOException err) {
@@ -519,6 +520,7 @@ class WalkFetchConnection extends BaseFetchConnection {
// an alternate.
//
recordError(id, err);
+ e1 = err;
continue;
} finally {
// If the pack was good its in the local repository
@@ -531,6 +533,9 @@ class WalkFetchConnection extends BaseFetchConnection {
if (pack.tmpIdx != null)
FileUtils.delete(pack.tmpIdx);
} catch (IOException e) {
+ if (e1 != null) {
+ e.addSuppressed(e1);
+ }
throw new TransportException(e.getMessage(), e);
}
packItr.remove();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory2.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory2.java
new file mode 100644
index 0000000000..88abc60162
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory2.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport.http;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * A {@link HttpConnectionFactory} that supports client-side sessions that can
+ * maintain state and configure connections.
+ *
+ * @since 5.11
+ */
+public interface HttpConnectionFactory2 extends HttpConnectionFactory {
+
+ /**
+ * Creates a new {@link GitSession} instance that can be used with
+ * connections created by this {@link HttpConnectionFactory} instance.
+ *
+ * @return a new {@link GitSession}
+ */
+ @NonNull
+ GitSession newSession();
+
+ /**
+ * A {@code GitSession} groups the multiple HTTP connections
+ * {@link org.eclipse.jgit.transport.TransportHttp TransportHttp} uses for
+ * the requests it makes during a git fetch or push. A {@code GitSession}
+ * can maintain client-side HTTPS state and can configure individual
+ * connections.
+ */
+ interface GitSession {
+
+ /**
+ * Configure a just created {@link HttpConnection}.
+ *
+ * @param connection
+ * to configure; created by the same
+ * {@link HttpConnectionFactory} instance
+ * @param sslVerify
+ * whether SSL is to be verified
+ * @return the configured {@connection}
+ * @throws IOException
+ * if the connection cannot be configured
+ * @throws GeneralSecurityException
+ * if the connection cannot be configured
+ */
+ @NonNull
+ HttpConnection configure(@NonNull HttpConnection connection,
+ boolean sslVerify) throws IOException, GeneralSecurityException;
+
+ /**
+ * Closes the {@link GitSession}, releasing any internal state.
+ */
+ void close();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnectionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnectionFactory.java
index b39f1579be..1b5d1b3c43 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnectionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnectionFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Christian Halstrick <christian.halstrick@sap.com> and others
+ * Copyright (C) 2013, 2020 Christian Halstrick <christian.halstrick@sap.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -12,6 +12,18 @@ package org.eclipse.jgit.transport.http;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.text.MessageFormat;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.transport.http.DelegatingSSLSocketFactory;
+import org.eclipse.jgit.util.HttpSupport;
/**
* A factory returning instances of
@@ -19,17 +31,70 @@ import java.net.URL;
*
* @since 3.3
*/
-public class JDKHttpConnectionFactory implements HttpConnectionFactory {
- /** {@inheritDoc} */
+public class JDKHttpConnectionFactory implements HttpConnectionFactory2 {
+
@Override
public HttpConnection create(URL url) throws IOException {
return new JDKHttpConnection(url);
}
- /** {@inheritDoc} */
@Override
public HttpConnection create(URL url, Proxy proxy)
throws IOException {
return new JDKHttpConnection(url, proxy);
}
+
+ @Override
+ public GitSession newSession() {
+ return new JdkConnectionSession();
+ }
+
+ private static class JdkConnectionSession implements GitSession {
+
+ private SSLContext securityContext;
+
+ private SSLSocketFactory socketFactory;
+
+ @Override
+ public JDKHttpConnection configure(HttpConnection connection,
+ boolean sslVerify) throws GeneralSecurityException {
+ if (!(connection instanceof JDKHttpConnection)) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().httpWrongConnectionType,
+ JDKHttpConnection.class.getName(),
+ connection.getClass().getName()));
+ }
+ JDKHttpConnection conn = (JDKHttpConnection) connection;
+ String scheme = conn.getURL().getProtocol();
+ if (!"https".equals(scheme) || sslVerify) { //$NON-NLS-1$
+ // sslVerify == true: use the JDK defaults
+ return conn;
+ }
+ if (securityContext == null) {
+ securityContext = SSLContext.getInstance("TLS"); //$NON-NLS-1$
+ TrustManager[] trustAllCerts = {
+ new NoCheckX509TrustManager() };
+ securityContext.init(null, trustAllCerts, null);
+ socketFactory = new DelegatingSSLSocketFactory(
+ securityContext.getSocketFactory()) {
+
+ @Override
+ protected void configure(SSLSocket socket) {
+ HttpSupport.configureTLS(socket);
+ }
+ };
+ }
+ conn.setHostnameVerifier((name, session) -> true);
+ ((HttpsURLConnection) conn.wrappedUrlConnection)
+ .setSSLSocketFactory(socketFactory);
+ return conn;
+ }
+
+ @Override
+ public void close() {
+ securityContext = null;
+ socketFactory = null;
+ }
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/NoCheckX509TrustManager.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/NoCheckX509TrustManager.java
new file mode 100644
index 0000000000..5cd4fb4c82
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/NoCheckX509TrustManager.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016, 2020 JGit contributors
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Contributors:
+ * Saša Živkov 2016 - initial API
+ * Thomas Wolf 2020 - extracted from HttpSupport
+ */
+package org.eclipse.jgit.transport.http;
+
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * A {@link X509TrustManager} that doesn't verify anything. Can be used for
+ * skipping SSL checks.
+ *
+ * @since 5.11
+ */
+public class NoCheckX509TrustManager implements X509TrustManager {
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] certs,
+ String authType) {
+ // no check
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] certs,
+ String authType) {
+ // no check
+ }
+} \ No newline at end of file
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 72278dc9c3..55b7d6279a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -2,7 +2,7 @@
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
* Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
* Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
- * Copyright (C) 2012-2020, Robin Rosenberg and others
+ * Copyright (C) 2012-2021, Robin Rosenberg and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -63,6 +63,7 @@ import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FS.ExecutionResult;
+import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.Holder;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.Paths;
@@ -799,7 +800,10 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
if (Constants.DOT_GIT.equals(name))
continue;
if (Constants.DOT_GIT_IGNORE.equals(name))
- ignoreNode = new PerDirectoryIgnoreNode(e);
+ ignoreNode = new PerDirectoryIgnoreNode(
+ TreeWalk.pathOf(path, 0, pathOffset)
+ + Constants.DOT_GIT_IGNORE,
+ e);
if (Constants.DOT_GIT_ATTRIBUTES.equals(name))
attributesNode = new PerDirectoryAttributesNode(e);
if (i != o)
@@ -983,8 +987,9 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return true;
} else if (ObjectId.zeroId().compareTo(idBuffer,
idOffset) == 0) {
- return new File(repository.getWorkTree(),
- entry.getPathString()).list().length > 0;
+ Path p = repository.getWorkTree().toPath()
+ .resolve(entry.getPathString());
+ return FileUtils.hasFiles(p);
}
return false;
} else if (mode == FileMode.SYMLINK.getBits())
@@ -1272,17 +1277,20 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
/** Magic type indicating we know rules exist, but they aren't loaded. */
private static class PerDirectoryIgnoreNode extends IgnoreNode {
- final Entry entry;
+ protected final Entry entry;
+
+ private final String name;
- PerDirectoryIgnoreNode(Entry entry) {
+ PerDirectoryIgnoreNode(String name, Entry entry) {
super(Collections.<FastIgnoreRule> emptyList());
+ this.name = name;
this.entry = entry;
}
IgnoreNode load() throws IOException {
IgnoreNode r = new IgnoreNode();
try (InputStream in = entry.openInputStream()) {
- r.parse(in);
+ r.parse(name, in);
}
return r.getRules().isEmpty() ? null : r;
}
@@ -1293,7 +1301,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
final Repository repository;
RootIgnoreNode(Entry entry, Repository repository) {
- super(entry);
+ super(entry != null ? entry.getName() : null, entry);
this.repository = repository;
}
@@ -1327,7 +1335,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
throws FileNotFoundException, IOException {
if (FS.DETECTED.exists(exclude)) {
try (FileInputStream in = new FileInputStream(exclude)) {
- r.parse(in);
+ r.parse(exclude.getAbsolutePath(), in);
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index d8cab358e7..0946f645fb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -22,7 +22,6 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
-import java.io.PrintStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.file.AccessDeniedException;
@@ -337,6 +336,9 @@ public abstract class FS {
try {
path = path.toAbsolutePath();
Path dir = Files.isDirectory(path) ? path : path.getParent();
+ if (dir == null) {
+ return FALLBACK_FILESTORE_ATTRIBUTES;
+ }
FileStoreAttributes cached = attrCacheByPath.get(dir);
if (cached != null) {
return cached;
@@ -511,7 +513,10 @@ public abstract class FS {
}
private static void write(Path p, String body) throws IOException {
- FileUtils.mkdirs(p.getParent().toFile(), true);
+ Path parent = p.getParent();
+ if (parent != null) {
+ FileUtils.mkdirs(parent.toFile(), true);
+ }
try (Writer w = new OutputStreamWriter(Files.newOutputStream(p),
UTF_8)) {
w.write(body);
@@ -1867,18 +1872,18 @@ public abstract class FS {
* @throws org.eclipse.jgit.api.errors.JGitInternalException
* if we fail to run the hook somehow. Causes may include an
* interrupted process or I/O errors.
- * @since 4.0
+ * @since 5.11
*/
public ProcessResult runHookIfPresent(Repository repository,
final String hookName,
- String[] args, PrintStream outRedirect, PrintStream errRedirect,
+ String[] args, OutputStream outRedirect, OutputStream errRedirect,
String stdinArgs) throws JGitInternalException {
return new ProcessResult(Status.NOT_SUPPORTED);
}
/**
* See
- * {@link #runHookIfPresent(Repository, String, String[], PrintStream, PrintStream, String)}
+ * {@link #runHookIfPresent(Repository, String, String[], OutputStream, OutputStream, String)}
* . Should only be called by FS supporting shell scripts execution.
*
* @param repository
@@ -1903,11 +1908,11 @@ public abstract class FS {
* @throws org.eclipse.jgit.api.errors.JGitInternalException
* if we fail to run the hook somehow. Causes may include an
* interrupted process or I/O errors.
- * @since 4.0
+ * @since 5.11
*/
protected ProcessResult internalRunHookIfPresent(Repository repository,
- final String hookName, String[] args, PrintStream outRedirect,
- PrintStream errRedirect, String stdinArgs)
+ final String hookName, String[] args, OutputStream outRedirect,
+ OutputStream errRedirect, String stdinArgs)
throws JGitInternalException {
File hookFile = findHook(repository, hookName);
if (hookFile == null || hookName == null) {
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 fb63dc02bb..946d81c73a 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
@@ -16,7 +16,7 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
-import java.io.PrintStream;
+import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileStore;
@@ -268,7 +268,7 @@ public class FS_POSIX extends FS {
/** {@inheritDoc} */
@Override
public ProcessResult runHookIfPresent(Repository repository, String hookName,
- String[] args, PrintStream outRedirect, PrintStream errRedirect,
+ String[] args, OutputStream outRedirect, OutputStream errRedirect,
String stdinArgs) throws JGitInternalException {
return internalRunHookIfPresent(repository, hookName, args, outRedirect,
errRedirect, stdinArgs);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
index d53bff78ea..add5498175 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
@@ -13,7 +13,7 @@ package org.eclipse.jgit.util;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.File;
-import java.io.PrintStream;
+import java.io.OutputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
@@ -139,7 +139,7 @@ public class FS_Win32_Cygwin extends FS_Win32 {
/** {@inheritDoc} */
@Override
public ProcessResult runHookIfPresent(Repository repository, String hookName,
- String[] args, PrintStream outRedirect, PrintStream errRedirect,
+ String[] args, OutputStream outRedirect, OutputStream errRedirect,
String stdinArgs) throws JGitInternalException {
return internalRunHookIfPresent(repository, hookName, args, outRedirect,
errRedirect, stdinArgs);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index aa39a44642..b9dd9baa61 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -43,6 +43,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
@@ -802,6 +803,23 @@ public class FileUtils {
}
/**
+ * Whether the path is a directory with files in it.
+ *
+ * @param dir
+ * directory path
+ * @return {@code true} if the given directory path contains files
+ * @throws IOException
+ * on any I/O errors accessing the path
+ *
+ * @since 5.11
+ */
+ public static boolean hasFiles(Path dir) throws IOException {
+ try (Stream<Path> stream = Files.list(dir)) {
+ return stream.findAny().isPresent();
+ }
+ }
+
+ /**
* Whether the given file can be executed.
*
* @param file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
index 04b3eab504..23a73faf8c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
@@ -24,21 +24,18 @@ import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
-import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.transport.http.HttpConnection;
+import org.eclipse.jgit.transport.http.NoCheckX509TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -301,40 +298,13 @@ public class HttpSupport {
*/
public static void disableSslVerify(HttpConnection conn)
throws IOException {
- final TrustManager[] trustAllCerts = new TrustManager[] {
- new DummyX509TrustManager() };
+ TrustManager[] trustAllCerts = {
+ new NoCheckX509TrustManager() };
try {
conn.configure(null, trustAllCerts, null);
- conn.setHostnameVerifier(new DummyHostnameVerifier());
+ conn.setHostnameVerifier((name, session) -> true);
} catch (KeyManagementException | NoSuchAlgorithmException e) {
- throw new IOException(e.getMessage());
- }
- }
-
- private static class DummyX509TrustManager implements X509TrustManager {
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return null;
- }
-
- @Override
- public void checkClientTrusted(X509Certificate[] certs,
- String authType) {
- // no check
- }
-
- @Override
- public void checkServerTrusted(X509Certificate[] certs,
- String authType) {
- // no check
- }
- }
-
- private static class DummyHostnameVerifier implements HostnameVerifier {
- @Override
- public boolean verify(String hostname, SSLSession session) {
- // always accept
- return true;
+ throw new IOException(e.getMessage(), e);
}
}
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 680d90392d..6d5694e435 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
@@ -246,7 +246,7 @@ public class IO {
* buffer that must be fully populated, [off, off+len).
* @param off
* position within the buffer to start writing to.
- * @return number of bytes in buffer or stream, whichever is shortest
+ * @return number of bytes read
* @throws java.io.IOException
* there was an error reading from the stream.
*/
@@ -254,8 +254,8 @@ public class IO {
throws IOException {
int r;
int len = 0;
- while ((r = fd.read(dst, off, dst.length - off)) >= 0
- && len < dst.length) {
+ while (off < dst.length
+ && (r = fd.read(dst, off, dst.length - off)) >= 0) {
off += r;
len += r;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java
new file mode 100644
index 0000000000..cf06172c17
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.util;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.GpgSignatureVerifier.TrustLevel;
+import org.eclipse.jgit.lib.PersonIdent;
+
+/**
+ * Utilities for signature verification.
+ *
+ * @since 5.11
+ */
+public final class SignatureUtils {
+
+ private SignatureUtils() {
+ // No instantiation
+ }
+
+ /**
+ * Writes information about a signature verification to a string.
+ *
+ * @param verification
+ * to show
+ * @param creator
+ * of the object verified; used for time zone information
+ * @param formatter
+ * to use for dates
+ * @return a textual representation of the {@link SignatureVerification},
+ * using LF as line separator
+ */
+ public static String toString(SignatureVerification verification,
+ PersonIdent creator, GitDateFormatter formatter) {
+ StringBuilder result = new StringBuilder();
+ // Use the creator's timezone for the signature date
+ PersonIdent dateId = new PersonIdent(creator,
+ verification.getCreationDate());
+ result.append(MessageFormat.format(JGitText.get().verifySignatureMade,
+ formatter.formatDate(dateId)));
+ result.append('\n');
+ result.append(MessageFormat.format(
+ JGitText.get().verifySignatureKey,
+ verification.getKeyFingerprint().toUpperCase(Locale.ROOT)));
+ result.append('\n');
+ if (!StringUtils.isEmptyOrNull(verification.getSigner())) {
+ result.append(
+ MessageFormat.format(JGitText.get().verifySignatureIssuer,
+ verification.getSigner()));
+ result.append('\n');
+ }
+ String msg;
+ if (verification.getVerified()) {
+ if (verification.isExpired()) {
+ msg = JGitText.get().verifySignatureExpired;
+ } else {
+ msg = JGitText.get().verifySignatureGood;
+ }
+ } else {
+ msg = JGitText.get().verifySignatureBad;
+ }
+ result.append(MessageFormat.format(msg, verification.getKeyUser()));
+ if (!TrustLevel.UNKNOWN.equals(verification.getTrustLevel())) {
+ result.append(' ' + MessageFormat
+ .format(JGitText.get().verifySignatureTrust, verification
+ .getTrustLevel().name().toLowerCase(Locale.ROOT)));
+ }
+ result.append('\n');
+ msg = verification.getMessage();
+ if (!StringUtils.isEmptyOrNull(msg)) {
+ result.append(msg);
+ result.append('\n');
+ }
+ return result.toString();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index 447f417e7e..54fd539f67 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -56,9 +56,9 @@ public abstract class SystemReader {
private static final SystemReader DEFAULT;
- private static Boolean isMacOS;
+ private static volatile Boolean isMacOS;
- private static Boolean isWindows;
+ private static volatile Boolean isWindows;
static {
SystemReader r = new Default();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
index 1f0fedda6b..562eb05dd9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
@@ -230,11 +230,16 @@ public abstract class TemporaryBuffer extends OutputStream {
if (Integer.MAX_VALUE < len)
throw new OutOfMemoryError(
JGitText.get().lengthExceedsMaximumArraySize);
- final byte[] out = new byte[(int) len];
+ int length = (int) len;
+ final byte[] out = new byte[length];
int outPtr = 0;
for (Block b : blocks) {
- System.arraycopy(b.buffer, 0, out, outPtr, b.count);
- outPtr += b.count;
+ int toCopy = Math.min(length - outPtr, b.count);
+ System.arraycopy(b.buffer, 0, out, outPtr, toCopy);
+ outPtr += toCopy;
+ if (outPtr == length) {
+ break;
+ }
}
return out;
}
@@ -461,6 +466,30 @@ public abstract class TemporaryBuffer extends OutputStream {
}
@Override
+ public byte[] toByteArray(int limit) throws IOException {
+ if (onDiskFile == null) {
+ return super.toByteArray(limit);
+ }
+ final long len = Math.min(length(), limit);
+ if (Integer.MAX_VALUE < len) {
+ throw new OutOfMemoryError(
+ JGitText.get().lengthExceedsMaximumArraySize);
+ }
+ final byte[] out = new byte[(int) len];
+ try (FileInputStream in = new FileInputStream(onDiskFile)) {
+ int read = 0;
+ int chunk;
+ while ((chunk = in.read(out, read, out.length - read)) >= 0) {
+ read += chunk;
+ if (read == out.length) {
+ break;
+ }
+ }
+ }
+ return out;
+ }
+
+ @Override
public void writeTo(OutputStream os, ProgressMonitor pm)
throws IOException {
if (onDiskFile == null) {
diff --git a/pom.xml b/pom.xml
index cf581feed7..bd362965c7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -151,8 +151,8 @@
<maven.compiler.target>1.8</maven.compiler.target>
<bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
- <jgit-last-release-version>5.9.0.202009080501-r</jgit-last-release-version>
- <apache-sshd-version>2.4.0</apache-sshd-version>
+ <jgit-last-release-version>5.10.0.202012080955-r</jgit-last-release-version>
+ <apache-sshd-version>2.6.0</apache-sshd-version>
<jsch-version>0.1.55</jsch-version>
<jzlib-version>1.1.1</jzlib-version>
<javaewah-version>1.1.7</javaewah-version>
@@ -162,20 +162,20 @@
<commons-compress-version>1.19</commons-compress-version>
<osgi-core-version>4.3.1</osgi-core-version>
<servlet-api-version>3.1.0</servlet-api-version>
- <jetty-version>9.4.30.v20200611</jetty-version>
- <japicmp-version>0.14.3</japicmp-version>
- <httpclient-version>4.5.10</httpclient-version>
- <httpcore-version>4.4.12</httpcore-version>
+ <jetty-version>9.4.36.v20210114</jetty-version>
+ <japicmp-version>0.14.4</japicmp-version>
+ <httpclient-version>4.5.13</httpclient-version>
+ <httpcore-version>4.4.14</httpcore-version>
<slf4j-version>1.7.30</slf4j-version>
<log4j-version>1.2.15</log4j-version>
<maven-javadoc-plugin-version>3.2.0</maven-javadoc-plugin-version>
<tycho-extras-version>1.7.0</tycho-extras-version>
- <gson-version>2.8.2</gson-version>
+ <gson-version>2.8.6</gson-version>
<bouncycastle-version>1.65</bouncycastle-version>
- <spotbugs-maven-plugin-version>4.1.3</spotbugs-maven-plugin-version>
+ <spotbugs-maven-plugin-version>4.2.0</spotbugs-maven-plugin-version>
<maven-project-info-reports-plugin-version>3.1.1</maven-project-info-reports-plugin-version>
<maven-jxr-plugin-version>3.0.0</maven-jxr-plugin-version>
- <maven-surefire-plugin-version>3.0.0-M4</maven-surefire-plugin-version>
+ <maven-surefire-plugin-version>3.0.0-M5</maven-surefire-plugin-version>
<maven-surefire-report-plugin-version>${maven-surefire-plugin-version}</maven-surefire-report-plugin-version>
<maven-compiler-plugin-version>3.8.1</maven-compiler-plugin-version>
<plexus-compiler-version>2.8.8</plexus-compiler-version>
@@ -299,7 +299,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
- <version>3.13.0</version>
+ <version>3.14.0</version>
<configuration>
<sourceEncoding>utf-8</sourceEncoding>
<minimumTokens>100</minimumTokens>
@@ -347,7 +347,7 @@
<dependency><!-- add support for ssh/scp -->
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-ssh</artifactId>
- <version>3.4.0</version>
+ <version>3.4.2</version>
</dependency>
</dependencies>
</plugin>
@@ -389,7 +389,7 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
- <version>2.1.5.RELEASE</version>
+ <version>2.4.1</version>
</plugin>
</plugins>
</pluginManagement>
@@ -408,7 +408,7 @@
<configuration>
<rules>
<requireMavenVersion>
- <version>3.6.2</version>
+ <version>3.6.3</version>
</requireMavenVersion>
</rules>
</configuration>
@@ -896,7 +896,7 @@
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
- <version>3.23.0</version>
+ <version>3.24.0</version>
</dependency>
</dependencies>
</plugin>