aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.bazelrc3
-rw-r--r--.mailmap13
-rw-r--r--Documentation/config-options.md18
-rw-r--r--WORKSPACE114
-rw-r--r--lib/BUILD26
-rw-r--r--org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF10
-rw-r--r--org.eclipse.jgit.ant.test/pom.xml2
-rw-r--r--org.eclipse.jgit.ant/META-INF/MANIFEST.MF8
-rw-r--r--org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.ant/pom.xml2
-rw-r--r--org.eclipse.jgit.archive/BUILD1
-rw-r--r--org.eclipse.jgit.archive/META-INF/MANIFEST.MF25
-rw-r--r--org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.archive/pom.xml7
-rw-r--r--org.eclipse.jgit.benchmarks/pom.xml7
-rw-r--r--org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/GetRefsBenchmark.java38
-rw-r--r--org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/RawTextBenchmark.java454
-rw-r--r--org.eclipse.jgit.coverage/pom.xml36
-rw-r--r--org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF19
-rw-r--r--org.eclipse.jgit.gpg.bc.test/pom.xml2
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeysTest.java62
-rw-r--r--org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF48
-rw-r--r--org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.gpg.bc/about.html26
-rw-r--r--org.eclipse.jgit.gpg.bc/pom.xml4
-rw-r--r--org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSigner1
-rw-r--r--org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.SignatureVerifierFactory (renamed from org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSignatureVerifierFactory)0
-rw-r--r--org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory1
-rw-r--r--org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties14
-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.java16
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgPublicKey.java9
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java138
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifierFactory.java23
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java128
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignerFactory.java31
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/OCBPBEProtectionRemoverFactory.java125
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SExprParser.java859
-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.java557
-rw-r--r--org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF12
-rw-r--r--org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.http.apache/pom.xml2
-rw-r--r--org.eclipse.jgit.http.server/BUILD7
-rw-r--r--org.eclipse.jgit.http.server/META-INF/MANIFEST.MF30
-rw-r--r--org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.http.server/pom.xml2
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java8
-rw-r--r--org.eclipse.jgit.http.test/META-INF/MANIFEST.MF42
-rw-r--r--org.eclipse.jgit.http.test/pom.xml2
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java3
-rw-r--r--org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF20
-rw-r--r--org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.junit.http/pom.xml2
-rw-r--r--org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF72
-rw-r--r--org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.junit.ssh/pom.xml2
-rw-r--r--org.eclipse.jgit.junit/.settings/.api_filters11
-rw-r--r--org.eclipse.jgit.junit/META-INF/MANIFEST.MF42
-rw-r--r--org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.junit/pom.xml2
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/FakeIndexFactory.java243
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java69
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java7
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SeparateClassloaderTestRunner.java2
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java51
-rw-r--r--org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF38
-rw-r--r--org.eclipse.jgit.lfs.server.test/pom.xml2
-rw-r--r--org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF32
-rw-r--r--org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.lfs.server/pom.xml2
-rw-r--r--org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF35
-rw-r--r--org.eclipse.jgit.lfs.test/pom.xml2
-rw-r--r--org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/internal/LfsConnectionFactoryTest.java6
-rw-r--r--org.eclipse.jgit.lfs/META-INF/MANIFEST.MF48
-rw-r--r--org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.lfs/pom.xml2
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java20
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml13
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml4
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml2
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target284
-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.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.target284
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.target288
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target288
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.target288
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.target288
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.tpd8
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd61
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20200831200620-2020-09.tpd66
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20201130205003-2020-12.tpd66
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210223232630-2021-03.tpd66
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210602031627-2021-06.tpd66
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210825222808-2021-09.tpd73
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211122181901-2021-12.tpd71
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211213173813-2021-12.tpd69
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220302172233-2022-03.tpd69
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220531185310-2022-06.tpd69
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220830213456-2022-09.tpd69
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20221123021534-2022-12.tpd69
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230302014618-2023-03.tpd27
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230531010532-2023-06.tpd25
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.29.tpd27
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.32.tpd (renamed from org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.31.tpd)10
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.33.tpd (renamed from org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd)10
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.34.tpd25
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.35.tpd25
-rw-r--r--org.eclipse.jgit.packaging/pom.xml30
-rw-r--r--org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF40
-rw-r--r--org.eclipse.jgit.pgm.test/pom.xml2
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java55
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java4
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java20
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java4
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/PackRefsTest.java81
-rw-r--r--org.eclipse.jgit.pgm/META-INF/MANIFEST.MF87
-rw-r--r--org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin2
-rw-r--r--org.eclipse.jgit.pgm/pom.xml2
-rw-r--r--org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties9
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java23
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java20
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java2
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java4
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java24
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java5
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java107
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/PackRefs.java34
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java57
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java5
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java5
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java3
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java7
-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.java2
-rw-r--r--org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF12
-rw-r--r--org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.ssh.apache.agent/pom.xml2
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.classpath5
-rw-r--r--org.eclipse.jgit.ssh.apache.test/.gitattributes2
-rw-r--r--org.eclipse.jgit.ssh.apache.test/BUILD50
-rw-r--r--org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF64
-rw-r--r--org.eclipse.jgit.ssh.apache.test/pom.xml8
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/allowed_signers2
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key7
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key27
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/expired.cert1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/no_principals.cert1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other-ca.cert1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other.cert1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/tester.cert1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/two_principals.cert1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krlbin0 -> 17185 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-allbin0 -> 792 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-cabin0 -> 104 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-certbin0 -> 182 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-emptybin0 -> 44 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-hashbin0 -> 445 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyidbin0 -> 7783 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid-wildbin0 -> 7732 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keysbin0 -> 654 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serialbin0 -> 382 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial-wildbin0 -> 162 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha1bin0 -> 313 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha256bin0 -> 445 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-text11
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-00017
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-00047
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-00107
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-00507
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-00907
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-05007
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-05107
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-05207
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-05507
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-07997
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-09997
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca7
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca27
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-hash11
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-keyid512
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-serials19
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha111
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha25611
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-00057
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-00097
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-00147
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-00167
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-00297
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-00497
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-00517
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-04997
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-08007
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-10107
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-10117
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key7
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/repo.bundlebin0 -> 5402 bytes
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key7
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key-cert.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key.pub1
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AbstractSshSignatureTest.java119
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AllowedSignersParseTest.java211
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrlLoadTest.java32
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshKrlTest.java146
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SerialRangeSetTest.java124
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtilsTest.java107
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifierTest.java151
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignerTest.java62
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/VerifyGitSignaturesTest.java154
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReaderTest.java29
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabaseTest.java586
-rw-r--r--org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java4
-rw-r--r--org.eclipse.jgit.ssh.apache/BUILD1
-rw-r--r--org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF142
-rw-r--r--org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.ssh.apache/pom.xml9
-rw-r--r--org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignatureVerifierFactory1
-rw-r--r--org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory1
-rw-r--r--org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties66
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/AllowedSigners.java530
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrl.java491
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshKrl.java120
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshSigningKeyDatabase.java161
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SerialRangeSet.java131
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SigningDatabase.java59
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtils.java175
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureConstants.java38
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifier.java319
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSigner.java485
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java13
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java7
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java200
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java13
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java68
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java15
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/CachingSigningKeyDatabase.java49
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SigningKeyDatabase.java94
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignatureVerifierFactory.java34
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignerFactory.java33
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/VerificationException.java63
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProviderFactory.java69
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java22
-rw-r--r--org.eclipse.jgit.ssh.apache/src/sun/security/x509/README.md3
-rw-r--r--org.eclipse.jgit.ssh.jsch.test/BUILD4
-rw-r--r--org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF17
-rw-r--r--org.eclipse.jgit.ssh.jsch.test/pom.xml2
-rw-r--r--org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/JSchSshProtocol2Test.java4
-rw-r--r--org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF20
-rw-r--r--org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.ssh.jsch/pom.xml2
-rw-r--r--org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/JschSession.java17
-rw-r--r--org.eclipse.jgit.test/BUILD5
-rw-r--r--org.eclipse.jgit.test/META-INF/MANIFEST.MF121
-rw-r--r--org.eclipse.jgit.test/exttst/org/eclipse/jgit/internal/storage/midx/CgitMidxCompatibilityTest.java208
-rw-r--r--org.eclipse.jgit.test/pom.xml2
-rw-r--r--org.eclipse.jgit.test/tests.bzl24
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds.patch10
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PostImage15
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PreImage8
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict.patch10
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PostImage15
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PreImage8
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_file_deleted.patch10
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.c43
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.javasource37
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.py26
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.rs27
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/sample.dtsi25
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java18
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java18
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java11
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java107
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java22
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java17
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LinkedWorktreeTest.java192
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java161
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java7
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java66
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java24
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java53
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java38
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerMissingPermissionsTest.java126
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerTest.java173
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java20
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java36
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java9
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java8
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java45
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameGeneratorCacheTest.java398
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameRegionMergerTest.java320
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterBuiltInDriverTest.java173
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/BareSuperprojectWriterTest.java45
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java69
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java88
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java26
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStatsTest.java210
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTableTest.java67
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfigTest.java312
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java53
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java98
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java44
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTableTest.java679
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BasePackWriterTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java)2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java197
-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/GcNumberOfPackFilesSinceBitmapStatisticsTest.java105
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java33
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcSinceBitmapStatisticsTest.java190
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java51
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java83
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java31
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java166
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java32
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java71
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java161
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java239
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexPeekIteratorTest.java71
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java38
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java14
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java41
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java10
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java16
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java13
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PersonIdentTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java38
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/GitlinkMergeTest.java5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmUnionTest.java328
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java70
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java62
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java36
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java123
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java9
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java7
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java218
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java16
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateIdentTest.java45
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java9
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java37
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackReachabilityTest.java16
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java118
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java11
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java46
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateFormatterTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserBadlyFormattedTest.java62
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserTest.java247
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_ParsePersonIdentTest.java50
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java8
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java19
-rw-r--r--org.eclipse.jgit.ui/META-INF/MANIFEST.MF18
-rw-r--r--org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.ui/pom.xml2
-rw-r--r--org.eclipse.jgit/.settings/.api_filters60
-rw-r--r--org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF149
-rw-r--r--org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit/pom.xml3
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties24
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java89
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java17
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java47
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java59
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java23
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PackRefsCommand.java87
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java21
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java41
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java25
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java92
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java45
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java57
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java127
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameRegionMerger.java158
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/BlameCache.java46
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/CacheRegion.java125
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffDriver.java116
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java144
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java49
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/Checkout.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java204
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java50
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java24
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java30
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java53
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriter.java77
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStats.java123
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTable.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java39
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java328
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheStats.java197
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTable.java187
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java40
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java30
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java34
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java274
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java75
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java76
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java33
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTable.java215
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackIndexWriter.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java)6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java98
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java40
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java118
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java79
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java166
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java54
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java515
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java82
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java152
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java52
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java53
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV1.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV2.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInserter.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java183
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java94
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java151
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexConstants.java55
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexPrettyPrinter.java156
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriter.java426
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java337
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackIndexWriter.java40
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java45
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java43
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/AbstractGpgSignatureVerifier.java71
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java17
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java120
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java265
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java115
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java137
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java215
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java101
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java83
-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.java184
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java92
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java141
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java17
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java150
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java72
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java96
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java51
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifier.java106
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifierFactory.java37
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifiers.java239
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Signer.java139
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/SignerFactory.java37
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Signers.java101
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java17
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java140
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ContentMergeStrategy.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java65
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java34
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java57
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java186
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java25
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java34
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java36
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java26
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java75
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/CommitTimeRevFilter.java89
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java21
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java62
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java21
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java112
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java41
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java49
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java16
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java31
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java152
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java80
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java33
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java88
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/GitTimeParser.java248
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java67
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java23
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java39
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/Stats.java25
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java38
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java80
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java38
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/time/ProposedTimestamp.java6
-rw-r--r--pom.xml112
-rw-r--r--tools/BUILD46
-rw-r--r--tools/workspace_status.py2
664 files changed, 24073 insertions, 12865 deletions
diff --git a/.bazelrc b/.bazelrc
index efa007a539..70322ddfcf 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -2,7 +2,7 @@
# https://issues.gerritcodereview.com/issues/303819949
common --noenable_bzlmod
-build --workspace_status_command="python ./tools/workspace_status.py"
+build --workspace_status_command="python3 ./tools/workspace_status.py"
build --repository_cache=~/.gerritcodereview/bazel-cache/repository
build --incompatible_strict_action_env
build --action_env=PATH
@@ -37,5 +37,6 @@ build:remote21 --config=remote
test --build_tests_only
test --test_output=errors
test --flaky_test_attempts=3
+test --test_tag_filters=-ext
import %workspace%/tools/remote-bazelrc
diff --git a/.mailmap b/.mailmap
index 7116ebb4b5..1c7739521d 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,20 +1,33 @@
+Adithya Chakilam <quic_achakila@quicinc.com> Adithya Chakilam <achakila@codeaurora.org>
Chris Aniszczyk <caniszczyk@gmail.com> Chris Aniszczyk <zx@eclipsesource.com>
Christian Halstrick <christian.halstrick@sap.com> Christian Halstrick <christian.halstrick@gmail.com>
Dani Megert <Daniel_Megert@ch.ibm.com> Daniel Megert <daniel_megert@ch.ibm.com>
+Dariusz Luksza <dariusz.luksza@gmail.com> Dariusz Luksza <dariusz@luksza.org>
+David Ostrovsky <david.ostrovsky@gmail.com> David Ostrovsky <david@ostrovsky.org>
David Pursehouse <david.pursehouse@gmail.com> David Pursehouse <david.pursehouse@sonymobile.com>
Han-Wen Nienhuys <hanwen@google.com> Han-Wen NIenhuys <hanwen@google.com>
Hector Oswaldo Caballero <hector.caballero@ericsson.com> Hector Caballero <hector.caballero@ericsson.com>
+Jackson Toeniskoetter <jackdt@google.com> <jackdt@google.com>
Lars Vogel <Lars.Vogel@vogella.com> Lars Vogel <Lars.Vogel@gmail.com>
Mark Ingram <markdingram@gmail.com> markdingram <markdingram@gmail.com>
Markus Duft <markus.duft@ssi-schaefer.com> Markus Duft <markus.duft@salomon.at>
+Martin Fick <mfick@nvidia.com> Martin Fick <mfick@codeaurora.org>
+Martin Fick <mfick@nvidia.com> Martin Fick <quic_mfick@quicinc.com>
Michael Keppler <michael.keppler@gmx.de> Michael Keppler <Michael.Keppler@gmx.de>
+Nasser Grainawi <quic_nasserg@quicinc.com> Nasser Grainawi <nasser@codeaurora.org>
Roberto Tyley <roberto.tyley@guardian.co.uk> roberto <roberto.tyley@guardian.co.uk>
Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <sasa.zivkov@sap.com>
Saša Živkov <sasa.zivkov@sap.com> Saša Živkov <zivkov@gmail.com>
Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <zivkov@gmail.com>
Sebastian Schuberth <sschuberth@gmail.com> Sebastian Schuberth <sebastian.schuberth@bosch.io>
+Sebastian Schuberth <sschuberth@gmail.com> <opensource@schuberth.dev>
Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <sop@google.com>
Shawn Pearce <spearce@spearce.org> Shawn Pearce <sop@google.com>
Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <spearce@spearce.org>
+Sven Selberg <sven.selberg@axis.com> Sven Selberg <sven.selberg@sonymobile.com>
Terry Parker <tparker@google.com> tparker <tparker@google.com>
Thomas Wolf <twolf@apache.org> Thomas Wolf <thomas.wolf@paranor.ch>
+Tomasz Zarna <tzarna@gmail.com> <Tomasz.Zarna@pl.ibm.com>
+Tomasz Zarna <tzarna@gmail.com> <tomasz.zarna@tasktop.com>
+Tobias Pfeifer <to.pfeifer@web.de> <to.pfeifer@sap.com>
+Yunjie Li <yunjieli@google.com> <yunjieli@google.com>
diff --git a/Documentation/config-options.md b/Documentation/config-options.md
index 78930e6267..4dde8f8c15 100644
--- a/Documentation/config-options.md
+++ b/Documentation/config-options.md
@@ -31,6 +31,7 @@ For details on native git options see also the official [git config documentatio
| `core.dfs.blockSize` | `64 kiB` | &#x20DE; | Size in bytes of a single window read in from the pack file into the DFS block cache. |
| `core.dfs.concurrencyLevel` | `32` | &#x20DE; | The estimated number of threads concurrently accessing the DFS block cache. |
| `core.dfs.deltaBaseCacheLimit` | `10 MiB` | &#x20DE; | Maximum number of bytes to hold in per-reader DFS delta base cache. |
+| `core.dfs.loadRevIndexInParallel` | false; | &#x20DE; | Try to load the reverse index in parallel with the bitmap index. |
| `core.dfs.streamFileThreshold` | `50 MiB` | &#x20DE; | The size threshold beyond which objects must be streamed. |
| `core.dfs.streamBuffer` | Block size of the pack | &#x20DE; | Number of bytes to use for buffering when streaming a pack file during copying. If 0 the block size of the pack is used|
| `core.dfs.streamRatio` | `0.30` | &#x20DE; | Ratio of DFS block cache to occupy with a copied pack. Values between `0` and `1.0`. |
@@ -54,9 +55,13 @@ For details on native git options see also the official [git config documentatio
| `core.streamFileThreshold` | `50 MiB` | &#x20DE; | The size threshold beyond which objects must be streamed. |
| `core.supportsAtomicFileCreation` | `true` | &#x20DE; | Whether the filesystem supports atomic file creation. |
| `core.symlinks` | Auto detect if filesystem supports symlinks| &#x2705; | If false, symbolic links are checked out as small plain files that contain the link text. |
-| `core.trustFolderStat` | `true` | &#x20DE; | Whether to trust the pack folder's, packed-refs file's and loose-objects folder's file attributes (Java equivalent of stat command on *nix). When looking for pack files, if `false` JGit will always scan the `.git/objects/pack` folder and if set to `true` it assumes that pack files are unchanged if the file attributes of the pack folder are unchanged. When getting the list of packed refs, if `false` JGit will always read the packed-refs file and if set to `true` it uses the file attributes of the packed-refs file and will only read it if a file attribute has changed. When looking for loose objects, if `false` and if a loose object is not found, JGit will open and close a stream to `.git/objects` folder (which can refresh its directory listing, at least on some NFS clients) and retry looking for that loose object. Setting this option to `false` can help to workaround caching issues on NFS, but reduces performance. |
-| `core.trustPackedRefsStat` | `unset` | &#x20DE; | Whether to trust the file attributes (Java equivalent of stat command on *nix) of the packed-refs file. If `never` JGit will ignore the file attributes of the packed-refs file and always read it. If `always` JGit will trust the file attributes of the packed-refs file and will only read it if a file attribute has changed. `after_open` behaves the same as `always`, except that the packed-refs file is opened and closed before its file attributes are considered. An open/close of the packed-refs file is known to refresh its file attributes, at least on some NFS clients. If `unset`, JGit will use the behavior described in `trustFolderStat`. |
-| `core.trustLooseRefStat` | `always` | &#x20DE; | Whether to trust the file attributes (Java equivalent of stat command on *nix) of the loose ref. If `always` JGit will trust the file attributes of the loose ref and its parent directories. `after_open` behaves similar to `always`, except that all parent directories of the loose ref up to the repository root are opened and closed before its file attributes are considered. An open/close of these parent directories is known to refresh the file attributes, at least on some NFS clients. |
+| ~~`core.trustFolderStat`~~ | `true` | &#x20DE; | __Deprecated__, use `core.trustStat` instead. If set to `true` translated to `core.trustStat=always`, if `false` translated to `core.trustStat=never`, see below. If both `core.trustFolderStat` and `core.trustStat` are configured then `trustStat` takes precedence and `trustFolderStat` is ignored. |
+| `core.trustLooseRefStat` | `inherit` | &#x20DE; | Whether to trust the file attributes of loose refs and its fan-out parent directory. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `trustStat`. |
+| `core.trustPackedRefsStat` | `inherit` | &#x20DE; | Whether to trust the file attributes of the packed-refs file. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. |
+| `core.trustTablesListStat` | `inherit` | &#x20DE; | Whether to trust the file attributes of the `tables.list` file used by the reftable ref storage backend to store the list of reftable filenames. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. The reftable backend is used if `extensions.refStorage = reftable`. |
+| `core.trustLooseObjectStat` | `inherit` | &#x20DE; | Whether to trust the file attributes of the loose object file and its fan-out parent directory. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. |
+| `core.trustPackStat` | `inherit` | &#x20DE; | Whether to trust the file attributes of the `objects/pack` directory. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. |
+| `core.trustStat` | `always` | &#x20DE; | Global option to configure whether to trust file attributes (Java equivalent of stat command on Unix) of files storing git objects. Can be overridden for specific files by configuring `core.trustLooseRefStat, core.trustPackedRefsStat, core.trustLooseObjectStat, core.trustPackStat,core.trustTablesListStat`. If `never` JGit will ignore the file attributes of the file and always read it. If `always` JGit will trust the file attributes and will only read it if a file attribute has changed. `after_open` behaves the same as `always`, but file attributes are only considered *after* the file itself and any transient parent directories have been opened and closed. An open/close of the file/directory is known to refresh its file attributes, at least on some NFS clients. |
| `core.worktree` | Root directory of the working tree if it is not the parent directory of the `.git` directory | &#x2705; | The path to the root of the working tree. |
## __fetch__ options
@@ -130,6 +135,13 @@ Proxy configuration uses the standard Java mechanisms via class `java.net.ProxyS
| `pack.window` | `10` | &#x2705; | Number of objects to try when looking for a delta base per thread searching for deltas. |
| `pack.windowMemory` | `0` (unlimited) | &#x2705; | Maximum number of bytes to put into the delta search window. |
+## reftable options
+
+| option | default | git option | description |
+|---------|---------|------------|-------------|
+| `reftable.autoRefresh` | `false` | &#x20DE; | Whether to auto-refresh the reftable stack if it is out of date. |
+
+
## __repack__ options
| option | default | git option | description |
diff --git a/WORKSPACE b/WORKSPACE
index ab80a64da6..505141c472 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -73,12 +73,6 @@ maven_jar(
)
maven_jar(
- name = "eddsa",
- artifact = "net.i2p.crypto:eddsa:0.3.0",
- sha1 = "1901c8d4d8bffb7d79027686cfb91e704217c3e1",
-)
-
-maven_jar(
name = "jsch",
artifact = "com.jcraft:jsch:0.1.55",
sha1 = "bbd40e5aa7aa3cfad5db34965456cee738a42a50",
@@ -108,44 +102,44 @@ maven_jar(
sha1 = "51cf043c87253c9f58b539c9f7e44c8894223850",
)
-SSHD_VERS = "2.12.0"
+SSHD_VERS = "2.15.0"
maven_jar(
name = "sshd-osgi",
artifact = "org.apache.sshd:sshd-osgi:" + SSHD_VERS,
- sha1 = "32b8de1cbb722ba75bdf9898e0c41d42af00ce57",
+ sha1 = "aa76898fe47eab7da0878dd60e6f3be5631e076c",
)
maven_jar(
name = "sshd-sftp",
artifact = "org.apache.sshd:sshd-sftp:" + SSHD_VERS,
- sha1 = "0f96f00a07b186ea62838a6a4122e8f4cad44df6",
+ sha1 = "2e226055ed060c64ed76256a9c45de6d0109eef8",
)
-JNA_VERS = "5.14.0"
+JNA_VERS = "5.16.0"
maven_jar(
name = "jna",
artifact = "net.java.dev.jna:jna:" + JNA_VERS,
- sha1 = "67bf3eaea4f0718cb376a181a629e5f88fa1c9dd",
+ sha1 = "ebea09f91dc9f7048099f963fb8d6f919f0a4d9c",
)
maven_jar(
name = "jna-platform",
artifact = "net.java.dev.jna:jna-platform:" + JNA_VERS,
- sha1 = "28934d48aed814f11e4c584da55c49fa7032b31b",
+ sha1 = "b2a9065f97c166893d504b164706512338e3bbc2",
)
maven_jar(
name = "commons-codec",
- artifact = "commons-codec:commons-codec:1.16.0",
- sha1 = "4e3eb3d79888d76b54e28b350915b5dc3919c9de",
+ artifact = "commons-codec:commons-codec:1.18.0",
+ sha1 = "ee45d1cf6ec2cc2b809ff04b4dc7aec858e0df8f",
)
maven_jar(
name = "commons-logging",
- artifact = "commons-logging:commons-logging:1.2",
- sha1 = "4bfc12adfe4842bf07b657f0369c4cb522955686",
+ artifact = "commons-logging:commons-logging:1.3.5",
+ sha1 = "a3fcc5d3c29b2b03433aa2d2f2d2c1b1638924a1",
)
maven_jar(
@@ -162,32 +156,38 @@ maven_jar(
maven_jar(
name = "servlet-api",
- artifact = "jakarta.servlet:jakarta.servlet-api:6.0.0",
- sha1 = "abecc699286e65035ebba9844c03931357a6a963",
+ artifact = "jakarta.servlet:jakarta.servlet-api:6.1.0",
+ sha1 = "1169a246913fe3823782af7943e7a103634867c5",
)
maven_jar(
name = "commons-compress",
- artifact = "org.apache.commons:commons-compress:1.26.0",
- sha1 = "659feffdd12280201c8aacb8f7be94f9a883c824",
+ artifact = "org.apache.commons:commons-compress:1.27.1",
+ sha1 = "a19151084758e2fbb6b41eddaa88e7b8ff4e6599",
+)
+
+maven_jar(
+ name = "commons-lang3",
+ artifact = "org.apache.commons:commons-lang3:3.17.0",
+ sha1 = "b17d2136f0460dcc0d2016ceefca8723bdf4ee70",
)
maven_jar(
name = "commons-io",
- artifact = "commons-io:commons-io:2.15.1",
- sha1 = "f11560da189ab563a5c8e351941415430e9304ea",
+ artifact = "commons-io:commons-io:2.18.0",
+ sha1 = "44084ef756763795b31c578403dd028ff4a22950",
)
maven_jar(
name = "tukaani-xz",
- artifact = "org.tukaani:xz:1.9",
- sha1 = "1ea4bec1a921180164852c65006d928617bd2caf",
+ artifact = "org.tukaani:xz:1.10",
+ sha1 = "1be8166f89e035a56c6bfc67dbc423996fe577e2",
)
maven_jar(
name = "args4j",
- artifact = "args4j:args4j:2.33",
- sha1 = "bd87a75374a6d6523de82fef51fc3cfe9baf9fc9",
+ artifact = "args4j:args4j:2.37",
+ sha1 = "244f60c057d72a785227c0562d3560f42a7ea54b",
)
maven_jar(
@@ -204,126 +204,114 @@ maven_jar(
maven_jar(
name = "mockito",
- artifact = "org.mockito:mockito-core:5.10.0",
- sha1 = "b3812fa2ee069f1d0b41c1c0155da79d0e1dcde0",
+ artifact = "org.mockito:mockito-core:5.15.2",
+ sha1 = "87be4b1e0cc5febc07ab3197a8ff3ede56b37a79",
)
maven_jar(
name = "assertj-core",
- artifact = "org.assertj:assertj-core:3.25.3",
- sha1 = "792b270e73aa1cfc28fa135be0b95e69ea451432",
+ artifact = "org.assertj:assertj-core:3.27.3",
+ sha1 = "31f5d58a202bd5df4993fb10fa2cffd610c20d6f",
)
-BYTE_BUDDY_VERSION = "1.14.12"
+BYTE_BUDDY_VERSION = "1.17.1"
maven_jar(
name = "bytebuddy",
artifact = "net.bytebuddy:byte-buddy:" + BYTE_BUDDY_VERSION,
- sha1 = "6e37f743dc15a8d7a4feb3eb0025cbc612d5b9e1",
+ sha1 = "8b5205fad48196a88d3d66dddff5a7417bce3596",
)
maven_jar(
name = "bytebuddy-agent",
artifact = "net.bytebuddy:byte-buddy-agent:" + BYTE_BUDDY_VERSION,
- sha1 = "be4984cb6fd1ef1d11f218a648889dfda44b8a15",
+ sha1 = "0669a13b59d5ffd8198a79e4dc99018a9278e457",
)
maven_jar(
name = "objenesis",
- artifact = "org.objenesis:objenesis:3.3",
- sha1 = "1049c09f1de4331e8193e579448d0916d75b7631",
+ artifact = "org.objenesis:objenesis:3.4",
+ sha1 = "675cbe121a68019235d27f6c34b4f0ac30e07418",
)
maven_jar(
name = "gson",
- artifact = "com.google.code.gson:gson:2.10.1",
- sha1 = "b3add478d4382b78ea20b1671390a858002feb6c",
+ artifact = "com.google.code.gson:gson:2.12.1",
+ sha1 = "4e773a317740b83b43cfc3d652962856041697cb",
)
-JETTY_VER = "12.0.9"
+JETTY_VER = "12.0.16"
maven_jar(
name = "jetty-servlet",
artifact = "org.eclipse.jetty.ee10:jetty-ee10-servlet:" + JETTY_VER,
- sha1 = "19e056d75741e7348411d677a9b26a54ea4b7d16",
- src_sha1 = "d7fcb4e9d259c1dd8595c6163054be449072fe14",
+ sha1 = "022a746c00b1ac5c790fee65a398c707160a46d8",
)
maven_jar(
name = "jetty-security",
artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VER,
- sha1 = "0c03a77f9d1a8b595cb4a83011cef735bd34bc95",
- src_sha1 = "9fba93bbce1466ef9c77d7a75338abd479641721",
+ sha1 = "23b1a3abecf9d6f5498064a32d9145ae1d8330f9",
)
maven_jar(
name = "jetty-server",
artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VER,
- sha1 = "87adc518dd68b674e08d7e4968d07b6dc73214f1",
- src_sha1 = "5404f097aae0126b820b040b4e924f31fe4ff25b",
+ sha1 = "3e3638b4bfbee04c27b3ae68e4949fc43b40a042",
)
maven_jar(
name = "jetty-session",
artifact = "org.eclipse.jetty:jetty-session:" + JETTY_VER,
- sha1 = "628444f02dfbc4efbd1920a12e055580b227e86b",
- src_sha1 = "2ac0ca2c84fa8e40655af1482a9c67d60d659ad2",
+ sha1 = "79cdedc7afebbdba4453f603dfe2f970baa35cc3",
)
maven_jar(
name = "jetty-http",
artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VER,
- sha1 = "cb54f006b1484306bc4b24dc3802cff472a6ba82",
- src_sha1 = "a1a6bc169e06007cabf6534fd7c7d1f2e91ab775",
+ sha1 = "68019fa90e8420ae15c109bd8c8611cacbaf43e5",
)
maven_jar(
name = "jetty-io",
artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VER,
- sha1 = "03e8f5b5c6d583ea591064671c23b8639c132052",
- src_sha1 = "41b5752f3aa4c77f872649c215142aee1d6a3395",
+ sha1 = "7a162c537a99bbaf35a074fec9a50815e6c81d9d",
)
maven_jar(
name = "jetty-util",
artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VER,
- sha1 = "996ebc69825af41d49e2edc6796eb714acdc3369",
- src_sha1 = "fe3b4ecf5a176bfd3c0055e5e1490503d90965e8",
+ sha1 = "e262e505363e5925df15618622d9888aefc1b0d0",
)
maven_jar(
name = "jetty-util-ajax",
artifact = "org.eclipse.jetty:jetty-util-ajax:" + JETTY_VER,
- sha1 = "634f67e811e0ad2acb32feaaf409927d80cd69ae",
- src_sha1 = "192f1e1254f659af64c6cd1124807fa12bdfa721",
+ sha1 = "60225034131e3f771b40bc75c15bd9cc4952302b",
)
-BOUNCYCASTLE_VER = "1.77"
+BOUNCYCASTLE_VER = "1.80"
maven_jar(
name = "bcpg",
artifact = "org.bouncycastle:bcpg-jdk18on:" + BOUNCYCASTLE_VER,
- sha1 = "bb0be51e8b378baae6e0d86f5282cd3887066539",
- src_sha1 = "33ff3269cede7165dac44033a3b150cc9f9f11cf",
+ sha1 = "163889a825393854dbe7dc52f1a8667e715e9859",
)
maven_jar(
name = "bcprov",
artifact = "org.bouncycastle:bcprov-jdk18on:" + BOUNCYCASTLE_VER,
- sha1 = "2cc971b6c20949c1ff98d1a4bc741ee848a09523",
- src_sha1 = "14ea9a3d759261358c6a1f59490ded125b5273a6",
+ sha1 = "e22100b41042decf09cab914a5af8d2c57b5ac4a",
)
maven_jar(
name = "bcutil",
artifact = "org.bouncycastle:bcutil-jdk18on:" + BOUNCYCASTLE_VER,
- sha1 = "de3eaef351545fe8562cf29ddff4a403a45b49b7",
- src_sha1 = "6f8f56ab009e7a3204817a0d45ed9638f5e30116",
+ sha1 = "b95726d1d49a0c65010c59a3e6640311d951bfd1",
)
maven_jar(
name = "bcpkix",
artifact = "org.bouncycastle:bcpkix-jdk18on:" + BOUNCYCASTLE_VER,
- sha1 = "ed953791ba0229747dd0fd9911e3d76a462acfd3",
- src_sha1 = "fdff397d5de0306db014f0a17e91717150db2768",
+ sha1 = "5277dfaaef2e92ce1d802499599a0ca7488f86e6",
)
diff --git a/lib/BUILD b/lib/BUILD
index c2a8271bbe..d236b3a8ee 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -20,6 +20,16 @@ java_library(
)
java_library(
+ name = "commons-lang3",
+ visibility = [
+ "//org.eclipse.jgit.archive:__pkg__",
+ "//org.eclipse.jgit.pgm.test:__pkg__",
+ "//org.eclipse.jgit.test:__pkg__",
+ ],
+ exports = ["@commons-lang3//jar"],
+)
+
+java_library(
name = "commons-io",
visibility = [
"//org.eclipse.jgit.archive:__pkg__",
@@ -45,16 +55,6 @@ java_library(
)
java_library(
- name = "eddsa",
- visibility = [
- "//org.eclipse.jgit.ssh.apache:__pkg__",
- "//org.eclipse.jgit.ssh.apache.test:__pkg__",
- "//org.eclipse.jgit.ssh.jsch.test:__pkg__",
- ],
- exports = ["@eddsa//jar"],
-)
-
-java_library(
name = "gson",
visibility = [
"//org.eclipse.jgit.lfs:__pkg__",
@@ -208,6 +208,8 @@ java_library(
visibility = [
"//org.eclipse.jgit.gpg.bc:__pkg__",
"//org.eclipse.jgit.gpg.bc.test:__pkg__",
+ "//org.eclipse.jgit.ssh.apache.test:__pkg__",
+ "//org.eclipse.jgit.ssh.jsch.test:__pkg__",
"//org.eclipse.jgit.test:__pkg__",
],
exports = ["@bcprov//jar"],
@@ -218,6 +220,8 @@ java_library(
visibility = [
"//org.eclipse.jgit.gpg.bc:__pkg__",
"//org.eclipse.jgit.gpg.bc.test:__pkg__",
+ "//org.eclipse.jgit.ssh.apache.test:__pkg__",
+ "//org.eclipse.jgit.ssh.jsch.test:__pkg__",
"//org.eclipse.jgit.test:__pkg__",
],
exports = ["@bcutil//jar"],
@@ -227,6 +231,8 @@ java_library(
name = "bcpkix",
visibility = [
"//org.eclipse.jgit.gpg.bc:__pkg__",
+ "//org.eclipse.jgit.ssh.apache.test:__pkg__",
+ "//org.eclipse.jgit.ssh.jsch.test:__pkg__",
"//org.eclipse.jgit.test:__pkg__",
],
exports = ["@bcpkix//jar"],
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index 7c079a5899..fa9a416363 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -5,13 +5,13 @@ Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.ant.test
Bundle-SymbolicName: org.eclipse.jgit.ant.test
Bundle-Vendor: %Bundle-Vendor
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.ant.tasks;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.hamcrest.core;version="[1.1.0,3.0.0)",
org.junit;version="[4.13,5.0.0)"
diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml
index be954bd8e0..4a54d87239 100644
--- a/org.eclipse.jgit.ant.test/pom.xml
+++ b/org.eclipse.jgit.ant.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ant.test</artifactId>
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index b6b52e595e..480d88e8d6 100644
--- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -3,13 +3,13 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.ant
Bundle-SymbolicName: org.eclipse.jgit.ant
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)"
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)"
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.ant;version="7.0.0",
- org.eclipse.jgit.ant.tasks;version="7.0.0";
+Export-Package: org.eclipse.jgit.ant;version="7.3.0",
+ org.eclipse.jgit.ant.tasks;version="7.3.0";
uses:="org.apache.tools.ant,
org.apache.tools.ant.types"
diff --git a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
index 22f8aff82c..136b7cf69a 100644
--- a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.ant - Sources
Bundle-SymbolicName: org.eclipse.jgit.ant.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ant;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ant;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index 7c3edf475c..4e85ee4f32 100644
--- a/org.eclipse.jgit.ant/pom.xml
+++ b/org.eclipse.jgit.ant/pom.xml
@@ -15,7 +15,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ant</artifactId>
diff --git a/org.eclipse.jgit.archive/BUILD b/org.eclipse.jgit.archive/BUILD
index 3d7dbd21f1..d4f53405d7 100644
--- a/org.eclipse.jgit.archive/BUILD
+++ b/org.eclipse.jgit.archive/BUILD
@@ -12,6 +12,7 @@ java_library(
resources = glob(["resources/**"]),
deps = [
"//lib:commons-compress",
+ "//lib:commons-lang3",
# We want these deps to be provided_deps
"//org.eclipse.jgit:jgit",
],
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index e1a3671819..fdfc635629 100644
--- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.archive
Bundle-SymbolicName: org.eclipse.jgit.archive
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -13,17 +13,18 @@ Import-Package: org.apache.commons.compress.archivers;version="[1.4,2.0)",
org.apache.commons.compress.compressors.bzip2;version="[1.4,2.0)",
org.apache.commons.compress.compressors.gzip;version="[1.4,2.0)",
org.apache.commons.compress.compressors.xz;version="[1.4,2.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.osgi.framework;version="[1.3.0,2.0.0)"
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.osgi.framework;version="[1.3.0,2.0.0)",
+ org.tukaani.xz
Bundle-ActivationPolicy: lazy
Bundle-Activator: org.eclipse.jgit.archive.FormatActivator
-Export-Package: org.eclipse.jgit.archive;version="7.0.0";
- uses:="org.eclipse.jgit.lib,
+Export-Package: org.eclipse.jgit.archive;version="7.3.0";
+ uses:="org.apache.commons.compress.archivers,
+ org.osgi.framework,
org.eclipse.jgit.api,
- org.apache.commons.compress.archivers,
- org.osgi.framework",
- org.eclipse.jgit.archive.internal;version="7.0.0";x-internal:=true
+ org.eclipse.jgit.lib",
+ org.eclipse.jgit.archive.internal;version="7.3.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 322f52f9f4..716e8d0917 100644
--- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.archive - Sources
Bundle-SymbolicName: org.eclipse.jgit.archive.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index 054aa59429..3935dfaca0 100644
--- a/org.eclipse.jgit.archive/pom.xml
+++ b/org.eclipse.jgit.archive/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.archive</artifactId>
@@ -41,6 +41,11 @@
</dependency>
<dependency>
+ <groupId>org.tukaani</groupId>
+ <artifactId>xz</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>${project.version}</version>
diff --git a/org.eclipse.jgit.benchmarks/pom.xml b/org.eclipse.jgit.benchmarks/pom.xml
index d8a616f09e..87d2bb31be 100644
--- a/org.eclipse.jgit.benchmarks/pom.xml
+++ b/org.eclipse.jgit.benchmarks/pom.xml
@@ -16,7 +16,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.benchmarks</artifactId>
@@ -52,6 +52,10 @@
<artifactId>org.eclipse.jgit.junit</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
</dependencies>
<build>
@@ -79,7 +83,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>${maven-compiler-plugin-version}</version>
<configuration>
<encoding>UTF-8</encoding>
<release>${java.version}</release>
diff --git a/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/GetRefsBenchmark.java b/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/GetRefsBenchmark.java
index 52a881bd11..44e862e7c8 100644
--- a/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/GetRefsBenchmark.java
+++ b/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/GetRefsBenchmark.java
@@ -24,10 +24,12 @@ import java.util.stream.IntStream;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.internal.storage.file.FileReftableDatabase;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig.TrustStat;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
@@ -38,8 +40,10 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
+import org.junit.Assume;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
@@ -66,11 +70,14 @@ public class GetRefsBenchmark {
@Param({ "true", "false" })
boolean useRefTable;
- @Param({ "100", "2500", "10000", "50000" })
+ @Param({ "true", "false" })
+ boolean autoRefresh;
+
+ @Param({ "100", "1000", "10000", "100000" })
int numBranches;
- @Param({ "true", "false" })
- boolean trustFolderStat;
+ @Param({ "ALWAYS", "AFTER_OPEN", "NEVER" })
+ TrustStat trustStat;
List<String> branches = new ArrayList<>(numBranches);
@@ -81,10 +88,13 @@ public class GetRefsBenchmark {
@Setup
@SuppressWarnings("boxing")
public void setupBenchmark() throws IOException, GitAPIException {
+ // if we use RefDirectory skip autoRefresh = false
+ Assume.assumeTrue(useRefTable || autoRefresh);
+
String firstBranch = "firstbranch";
testDir = Files.createDirectory(Paths.get("testrepos"));
- String repoName = "branches-" + numBranches + "-trustFolderStat-"
- + trustFolderStat + "-" + refDatabaseType();
+ String repoName = "branches-" + numBranches + "-trustStat-"
+ + trustStat + "-" + refDatabaseType();
Path workDir = testDir.resolve(repoName);
Path repoPath = workDir.resolve(".git");
Git git = Git.init().setDirectory(workDir.toFile()).call();
@@ -97,10 +107,13 @@ public class GetRefsBenchmark {
((FileRepository) git.getRepository()).convertRefStorage(
ConfigConstants.CONFIG_REF_STORAGE_REFTABLE, false,
false);
+ FileReftableDatabase refdb = (FileReftableDatabase) git
+ .getRepository().getRefDatabase();
+ refdb.setAutoRefresh(autoRefresh);
} else {
- cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT,
- trustFolderStat);
+ cfg.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_TRUST_STAT,
+ trustStat);
}
cfg.setInt(ConfigConstants.CONFIG_RECEIVE_SECTION, null,
"maxCommandBytes", Integer.MAX_VALUE);
@@ -112,7 +125,8 @@ public class GetRefsBenchmark {
System.out.println("Preparing test");
System.out.println("- repository: \t\t" + repoPath);
System.out.println("- refDatabase: \t\t" + refDatabaseType());
- System.out.println("- trustFolderStat: \t" + trustFolderStat);
+ System.out.println("- autoRefresh: \t\t" + autoRefresh);
+ System.out.println("- trustStat: \t" + trustStat);
System.out.println("- branches: \t\t" + numBranches);
BatchRefUpdate u = repo.getRefDatabase().newBatchUpdate();
@@ -152,7 +166,8 @@ public class GetRefsBenchmark {
@BenchmarkMode({ Mode.AverageTime })
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 2, time = 100, timeUnit = TimeUnit.MILLISECONDS)
- @Measurement(iterations = 2, time = 10, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(2)
public void testGetExactRef(Blackhole blackhole, BenchmarkState state)
throws IOException {
String branchName = state.branches
@@ -164,7 +179,8 @@ public class GetRefsBenchmark {
@BenchmarkMode({ Mode.AverageTime })
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 2, time = 100, timeUnit = TimeUnit.MILLISECONDS)
- @Measurement(iterations = 2, time = 10, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(2)
public void testGetRefsByPrefix(Blackhole blackhole, BenchmarkState state)
throws IOException {
String branchPrefix = "refs/heads/branch/" + branchIndex.nextInt(100)
diff --git a/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/RawTextBenchmark.java b/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/RawTextBenchmark.java
new file mode 100644
index 0000000000..19297ebebb
--- /dev/null
+++ b/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/RawTextBenchmark.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2022, Matthias Sohn <matthias.sohn@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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.benchmarks;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.eclipse.jgit.diff.RawText.getBufferSize;
+import static org.eclipse.jgit.diff.RawText.isBinary;
+import static org.eclipse.jgit.diff.RawText.isCrLfText;
+
+@State(Scope.Thread)
+public class RawTextBenchmark {
+
+ @State(Scope.Benchmark)
+ public static class BenchmarkState {
+
+ @Param({"1", "2", "3", "4", "5", "6"})
+ int testIndex;
+
+ @Param({"false", "true"})
+ boolean complete;
+
+ byte[] bytes;
+
+ @Setup
+ public void setupBenchmark() {
+ switch (testIndex) {
+ case 1: {
+ byte[] tmpBytes = "a".repeat(102400).getBytes();
+ bytes = tmpBytes;
+ break;
+ }
+ case 2: {
+ byte[] tmpBytes = "a".repeat(102400).getBytes();
+ byte[] tmpBytes2 = new byte[tmpBytes.length + 1];
+ System.arraycopy(tmpBytes, 0, tmpBytes2, 0, tmpBytes.length);
+ tmpBytes2[500] = '\0';
+ tmpBytes2[tmpBytes.length] = '\0';
+ bytes = tmpBytes2;
+ break;
+ }
+ case 3: {
+ byte[] tmpBytes = "a".repeat(102400).getBytes();
+ byte[] tmpBytes2 = new byte[tmpBytes.length + 1];
+ System.arraycopy(tmpBytes, 0, tmpBytes2, 0, tmpBytes.length);
+ tmpBytes2[500] = '\r';
+ tmpBytes2[tmpBytes.length] = '\r';
+ bytes = tmpBytes2;
+ break;
+ }
+ case 4: {
+ byte[] tmpBytes = "a".repeat(102400).getBytes();
+ byte[] tmpBytes2 = new byte[tmpBytes.length + 1];
+ System.arraycopy(tmpBytes, 0, tmpBytes2, 0, tmpBytes.length);
+ tmpBytes2[499] = '\r';
+ tmpBytes2[500] = '\n';
+ tmpBytes2[tmpBytes.length - 1] = '\r';
+ tmpBytes2[tmpBytes.length] = '\n';
+ bytes = tmpBytes2;
+ break;
+ }
+ case 5: {
+ byte[] tmpBytes = "a".repeat(102400).getBytes();
+ tmpBytes[0] = '\0';
+ bytes = tmpBytes;
+ break;
+ }
+ case 6: {
+ byte[] tmpBytes = "a".repeat(102400).getBytes();
+ tmpBytes[0] = '\r';
+ bytes = tmpBytes;
+ break;
+ }
+ default:
+ }
+ }
+
+ @TearDown
+ public void teardown() {
+ }
+ }
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsCrLfTextOld(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isCrLfTextOld(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsCrLfTextNewCandidate1(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isCrLfTextNewCandidate1(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsCrLfTextNewCandidate2(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isCrLfTextNewCandidate2(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsCrLfTextNewCandidate3(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isCrLfTextNewCandidate3(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsCrLfTextNew(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isCrLfText(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsBinaryOld(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isBinaryOld(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsBinaryNew(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isBinary(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+
+ /**
+ * Determine heuristically whether a byte array represents binary (as
+ * opposed to text) content.
+ *
+ * @param raw
+ * the raw file content.
+ * @param length
+ * number of bytes in {@code raw} to evaluate. This should be
+ * {@code raw.length} unless {@code raw} was over-allocated by
+ * the caller.
+ * @param complete
+ * whether {@code raw} contains the whole data
+ * @return true if raw is likely to be a binary file, false otherwise
+ * @since 6.0
+ */
+ public static boolean isBinaryOld(byte[] raw, int length, boolean complete) {
+ // Similar heuristic as C Git. Differences:
+ // - limited buffer size; may be only the beginning of a large blob
+ // - no counting of printable vs. non-printable bytes < 0x20 and 0x7F
+ int maxLength = getBufferSize();
+ boolean isComplete = complete;
+ if (length > maxLength) {
+ // We restrict the length in all cases to getBufferSize() to get
+ // predictable behavior. Sometimes we load streams, and sometimes we
+ // have the full data in memory. With streams, we never look at more
+ // than the first getBufferSize() bytes. If we looked at more when
+ // we have the full data, different code paths in JGit might come to
+ // different conclusions.
+ length = maxLength;
+ isComplete = false;
+ }
+ byte last = 'x'; // Just something inconspicuous.
+ for (int ptr = 0; ptr < length; ptr++) {
+ byte curr = raw[ptr];
+ if (isBinary(curr, last)) {
+ return true;
+ }
+ last = curr;
+ }
+ if (isComplete) {
+ // Buffer contains everything...
+ return last == '\r'; // ... so this must be a lone CR
+ }
+ return false;
+ }
+
+ /**
+ * Determine heuristically whether a byte array represents text content
+ * using CR-LF as line separator.
+ *
+ * @param raw the raw file content.
+ * @param length number of bytes in {@code raw} to evaluate.
+ * @param complete whether {@code raw} contains the whole data
+ * @return {@code true} if raw is likely to be CR-LF delimited text,
+ * {@code false} otherwise
+ * @since 6.0
+ */
+ public static boolean isCrLfTextOld(byte[] raw, int length, boolean complete) {
+ boolean has_crlf = false;
+ byte last = 'x'; // Just something inconspicuous
+ for (int ptr = 0; ptr < length; ptr++) {
+ byte curr = raw[ptr];
+ if (isBinary(curr, last)) {
+ return false;
+ }
+ if (curr == '\n' && last == '\r') {
+ has_crlf = true;
+ }
+ last = curr;
+ }
+ if (last == '\r') {
+ if (complete) {
+ // Lone CR: it's binary after all.
+ return false;
+ }
+ // Tough call. If the next byte, which we don't have, would be a
+ // '\n', it'd be a CR-LF text, otherwise it'd be binary. Just decide
+ // based on what we already scanned; it wasn't binary until now.
+ }
+ return has_crlf;
+ }
+
+ /**
+ * Determine heuristically whether a byte array represents text content
+ * using CR-LF as line separator.
+ *
+ * @param raw
+ * the raw file content.
+ * @param length
+ * number of bytes in {@code raw} to evaluate.
+ * @return {@code true} if raw is likely to be CR-LF delimited text,
+ * {@code false} otherwise
+ * @param complete
+ * whether {@code raw} contains the whole data
+ * @since 6.0
+ */
+ public static boolean isCrLfTextNewCandidate1(byte[] raw, int length, boolean complete) {
+ boolean has_crlf = false;
+
+ // first detect empty
+ if (length <= 0) {
+ return false;
+ }
+
+ // next detect '\0'
+ for (int reversePtr = length - 1; reversePtr >= 0; --reversePtr) {
+ if (raw[reversePtr] == '\0') {
+ return false;
+ }
+ }
+
+ // if '\r' be last, then if complete then return non-crlf
+ if (raw[length - 1] == '\r' && complete) {
+ return false;
+ }
+
+ for (int ptr = 0; ptr < length - 1; ptr++) {
+ byte curr = raw[ptr];
+ if (curr == '\r') {
+ byte next = raw[ptr + 1];
+ if (next != '\n') {
+ return false;
+ }
+ // else
+ // we have crlf here
+ has_crlf = true;
+ // as next is '\n', it can never be '\r', just skip it from next check
+ ++ptr;
+ }
+ }
+
+ return has_crlf;
+ }
+
+ /**
+ * Determine heuristically whether a byte array represents text content
+ * using CR-LF as line separator.
+ *
+ * @param raw
+ * the raw file content.
+ * @param length
+ * number of bytes in {@code raw} to evaluate.
+ * @return {@code true} if raw is likely to be CR-LF delimited text,
+ * {@code false} otherwise
+ * @param complete
+ * whether {@code raw} contains the whole data
+ * @since 6.0
+ */
+ public static boolean isCrLfTextNewCandidate2(byte[] raw, int length, boolean complete) {
+ boolean has_crlf = false;
+
+ // first detect empty
+ if (length <= 0) {
+ return false;
+ }
+
+ // if '\r' be last, then if complete then return non-crlf
+ byte last = raw[length - 1];
+ if (last == '\0' || last == '\r' && complete) {
+ return false;
+ }
+
+ for (int ptr = 0; ptr < length - 1; ptr++) {
+ byte b = raw[ptr];
+ switch (b) {
+ case '\0':
+ return false;
+ case '\r': {
+ ++ptr;
+ b = raw[ptr];
+ if (b != '\n') {
+ return false;
+ }
+ // else
+ // we have crlf here
+ has_crlf = true;
+ // as next is '\n', it can never be '\r', just skip it from next check
+ break;
+ }
+ default:
+ // do nothing;
+ break;
+ }
+ }
+
+ return has_crlf;
+ }
+
+ /**
+ * Determine heuristically whether a byte array represents text content
+ * using CR-LF as line separator.
+ *
+ * @param raw
+ * the raw file content.
+ * @param length
+ * number of bytes in {@code raw} to evaluate.
+ * @return {@code true} if raw is likely to be CR-LF delimited text,
+ * {@code false} otherwise
+ * @param complete
+ * whether {@code raw} contains the whole data
+ * @since 6.0
+ */
+ public static boolean isCrLfTextNewCandidate3(byte[] raw, int length, boolean complete) {
+ boolean has_crlf = false;
+
+ int ptr = -1;
+ byte current;
+ while (ptr < length - 2) {
+ current = raw[++ptr];
+ if ('\0' == current || '\r' == current && (raw[++ptr] != '\n' || !(has_crlf = true))) {
+ return false;
+ }
+ }
+
+ if (ptr == length - 2) {
+ // if '\r' be last, then if isComplete then return binary
+ current = raw[++ptr];
+ if('\0' == current || '\r' == current && complete){
+ return false;
+ }
+ }
+
+ return has_crlf;
+ }
+
+
+ public static void main(String[] args) throws RunnerException {
+ Options opt = new OptionsBuilder()
+ .include(RawTextBenchmark.class.getSimpleName())
+ .forks(1).jvmArgs("-ea").build();
+ new Runner(opt).run();
+ }
+}
diff --git a/org.eclipse.jgit.coverage/pom.xml b/org.eclipse.jgit.coverage/pom.xml
index 1fe5318660..8cab250c67 100644
--- a/org.eclipse.jgit.coverage/pom.xml
+++ b/org.eclipse.jgit.coverage/pom.xml
@@ -14,7 +14,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -27,88 +27,88 @@
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ant</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.archive</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.http.apache</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.http.server</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.lfs</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.lfs.server</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.pgm</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ui</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ssh.apache</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ant.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.http.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.pgm.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.lfs.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
</dependencies>
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 27510e2095..287d75f92a 100644
--- a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
@@ -3,19 +3,20 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.gpg.bc.test
Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
Require-Bundle: org.hamcrest.core;bundle-version="[1.3.0,2.0.0)"
-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="[7.0.0,7.1.0)",
- org.eclipse.jgit.gpg.bc.internal.keys;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.sha1;version="[7.0.0,7.1.0)",
+Import-Package: org.bouncycastle.asn1.cryptlib;version="[1.79.0,2.0.0)",
+ org.bouncycastle.jce.provider;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp.operator;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp.operator.jcajce;version="[1.79.0,2.0.0)",
+ org.bouncycastle.util.encoders;version="[1.79.0,2.0.0)",
+ org.eclipse.jgit.gpg.bc.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.sha1;version="[7.3.0,7.4.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)"
diff --git a/org.eclipse.jgit.gpg.bc.test/pom.xml b/org.eclipse.jgit.gpg.bc.test/pom.xml
index d865b13901..10aa742a10 100644
--- a/org.eclipse.jgit.gpg.bc.test/pom.xml
+++ b/org.eclipse.jgit.gpg.bc.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.gpg.bc.test</artifactId>
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
index fed06103b6..d486c977f0 100644
--- 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.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
@@ -9,10 +9,7 @@
*/
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;
@@ -20,8 +17,6 @@ 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;
@@ -49,39 +44,15 @@ public class SecretKeysTest {
}
}
- 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) {
+ TestData(String name, boolean encrypted) {
this.name = name;
this.encrypted = encrypted;
- this.keyValue = keyValue;
}
@Override
@@ -93,19 +64,12 @@ public class SecretKeysTest {
@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("62D43D7F117F7A5E4998ECB6617EE9942D069C14", 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);
- }
+ new TestData("AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11", false),
+ new TestData("2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A", false),
+ new TestData("66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9", true),
+ new TestData("F727FAB884DA3BD402B6E0F5472E108D21033124", true),
+ new TestData("62D43D7F117F7A5E4998ECB6617EE9942D069C14", true),
+ new TestData("faked", false) };
}
private static PGPPublicKey readAsc(InputStream in)
@@ -131,11 +95,6 @@ public class SecretKeysTest {
@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) {
@@ -151,11 +110,6 @@ public class SecretKeysTest {
: null,
publicKey);
assertNotNull(secretKey);
- } catch (PGPException e) {
- // Currently we may not be able to load OCB-encrypted keys.
- assertTrue(e.toString(), 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 9749ac1111..f35e5e5a3f 100644
--- a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
@@ -3,34 +3,28 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.gpg.bc
Bundle-SymbolicName: org.eclipse.jgit.gpg.bc;singleton:=true
-Fragment-Host: org.eclipse.jgit;bundle-version="[7.0.0,7.1.0)"
+Fragment-Host: org.eclipse.jgit;bundle-version="[7.3.0,7.4.0)"
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: OSGI-INF/l10n/gpg_bc
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Import-Package: org.bouncycastle.asn1;version="[1.69.0,2.0.0)",
- org.bouncycastle.asn1.x9;version="[1.69.0,2.0.0)",
- org.bouncycastle.bcpg;version="[1.69.0,2.0.0)",
- org.bouncycastle.bcpg.sig;version="[1.69.0,2.0.0)",
- org.bouncycastle.crypto.ec;version="[1.69.0,2.0.0)",
- org.bouncycastle.gpg;version="[1.69.0,2.0.0)",
- org.bouncycastle.gpg.keybox;version="[1.69.0,2.0.0)",
- org.bouncycastle.gpg.keybox.jcajce;version="[1.69.0,2.0.0)",
- org.bouncycastle.jcajce.interfaces;version="[1.69.0,2.0.0)",
- org.bouncycastle.jcajce.util;version="[1.69.0,2.0.0)",
- org.bouncycastle.jce.provider;version="[1.69.0,2.0.0)",
- org.bouncycastle.math.ec;version="[1.69.0,2.0.0)",
- org.bouncycastle.math.field;version="[1.69.0,2.0.0)",
- org.bouncycastle.openpgp;version="[1.69.0,2.0.0)",
- org.bouncycastle.openpgp.jcajce;version="[1.69.0,2.0.0)",
- org.bouncycastle.openpgp.operator;version="[1.69.0,2.0.0)",
- org.bouncycastle.openpgp.operator.jcajce;version="[1.69.0,2.0.0)",
- org.bouncycastle.util;version="[1.69.0,2.0.0)",
- org.bouncycastle.util.encoders;version="[1.69.0,2.0.0)",
- org.bouncycastle.util.io;version="[1.69.0,2.0.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
+Import-Package: org.bouncycastle.asn1;version="[1.79.0,2.0.0)",
+ org.bouncycastle.asn1.x9;version="[1.79.0,2.0.0)",
+ org.bouncycastle.bcpg;version="[1.79.0,2.0.0)",
+ org.bouncycastle.bcpg.sig;version="[1.79.0,2.0.0)",
+ org.bouncycastle.crypto.ec;version="[1.79.0,2.0.0)",
+ org.bouncycastle.gpg;version="[1.79.0,2.0.0)",
+ org.bouncycastle.gpg.keybox;version="[1.79.0,2.0.0)",
+ org.bouncycastle.gpg.keybox.jcajce;version="[1.79.0,2.0.0)",
+ org.bouncycastle.jcajce.interfaces;version="[1.79.0,2.0.0)",
+ org.bouncycastle.jcajce.util;version="[1.79.0,2.0.0)",
+ org.bouncycastle.math.ec;version="[1.79.0,2.0.0)",
+ org.bouncycastle.math.field;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp.jcajce;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp.operator;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp.operator.jcajce;version="[1.79.0,2.0.0)",
+ org.bouncycastle.util.encoders;version="[1.79.0,2.0.0)",
org.slf4j;version="[1.7.0,3.0.0)"
-Export-Package: org.eclipse.jgit.gpg.bc;version="7.0.0",
- org.eclipse.jgit.gpg.bc.internal;version="7.0.0";x-friends:="org.eclipse.jgit.gpg.bc.test",
- org.eclipse.jgit.gpg.bc.internal.keys;version="7.0.0";x-friends:="org.eclipse.jgit.gpg.bc.test"
+Export-Package: org.eclipse.jgit.gpg.bc.internal;version="7.3.0";x-friends:="org.eclipse.jgit.gpg.bc.test",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="7.3.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 0733a68ba0..5134716681 100644
--- a/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.gpg.bc - Sources
Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.gpg.bc/about.html b/org.eclipse.jgit.gpg.bc/about.html
index fc527d5a3a..92b9409831 100644
--- a/org.eclipse.jgit.gpg.bc/about.html
+++ b/org.eclipse.jgit.gpg.bc/about.html
@@ -58,32 +58,6 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.</p>
-<hr>
-<p><b>org.eclipse.jgit.gpg.bc.internal.keys.SExprParser - MIT</b></p>
-
-<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>
-
</body>
</html>
diff --git a/org.eclipse.jgit.gpg.bc/pom.xml b/org.eclipse.jgit.gpg.bc/pom.xml
index f4dce68fab..6159129295 100644
--- a/org.eclipse.jgit.gpg.bc/pom.xml
+++ b/org.eclipse.jgit.gpg.bc/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.gpg.bc</artifactId>
@@ -160,7 +160,7 @@
<breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
<onlyBinaryIncompatible>false</onlyBinaryIncompatible>
<includeSynthetic>false</includeSynthetic>
- <ignoreMissingClasses>false</ignoreMissingClasses>
+ <ignoreMissingClasses>true</ignoreMissingClasses>
<skipPomModules>true</skipPomModules>
</parameter>
<skip>false</skip>
diff --git a/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSigner b/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSigner
deleted file mode 100644
index 6752b64ddf..0000000000
--- a/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSigner
+++ /dev/null
@@ -1 +0,0 @@
-org.eclipse.jgit.gpg.bc.internal.BouncyCastleGpgSigner
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.SignatureVerifierFactory
index 17ab30fba7..17ab30fba7 100644
--- 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.SignatureVerifierFactory
diff --git a/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory b/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory
new file mode 100644
index 0000000000..c0b214db39
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory
@@ -0,0 +1 @@
+org.eclipse.jgit.gpg.bc.internal.BouncyCastleGpgSignerFactory
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 77ca2cd0a4..9e7f98cab1 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,7 +1,5 @@
corrupt25519Key=Ed25519/Curve25519 public key has wrong length: {0}
credentialPassphrase=Passphrase
-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
@@ -9,22 +7,14 @@ 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
+keyAlgorithmMismatch=Secret key has a different algorithm than the public key
+keyMismatch=Secret key does not match public key; public key is {0} {1} while secret key is for {2} {3}
logWarnGnuPGHome=Cannot access GPG home directory given by environment variable GNUPGHOME={}
logWarnGpgHomeProperty=Cannot access GPG home directory given by Java system property jgit.gpg.home={}
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
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
deleted file mode 100644
index fdd1a2b11a..0000000000
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/BouncyCastleGpgSignerFactory.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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 705e195e44..fcae7c2b98 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,5 +1,5 @@
/*
- * Copyright (C) 2018, 2021 Salesforce and others
+ * Copyright (C) 2018, 2024 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
@@ -30,8 +30,6 @@ 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;
@@ -39,22 +37,14 @@ public final class BCText extends TranslationBundle {
/***/ public String gpgNoKeyInLegacySecring;
/***/ public String gpgNoPublicKeyFound;
/***/ public String gpgNoSecretKeyForPublicKey;
- /***/ public String gpgNoSuchAlgorithm;
/***/ public String gpgNotASigningKey;
/***/ public String gpgKeyInfo;
/***/ public String gpgSigningCancelled;
+ /***/ public String keyAlgorithmMismatch;
+ /***/ public String keyMismatch;
/***/ public String logWarnGnuPGHome;
/***/ public String logWarnGpgHomeProperty;
/***/ 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;
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgPublicKey.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgPublicKey.java
index d736536fd7..9ec5b45530 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgPublicKey.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgPublicKey.java
@@ -1,3 +1,12 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
package org.eclipse.jgit.gpg.bc.internal;
import java.util.List;
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
index 3378bb3969..5a3d43ba54 100644
--- 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
@@ -12,7 +12,6 @@ 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.Date;
@@ -20,7 +19,6 @@ import java.util.List;
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;
@@ -31,33 +29,20 @@ 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.api.errors.JGitInternalException;
-import org.eclipse.jgit.lib.AbstractGpgSignatureVerifier;
import org.eclipse.jgit.lib.GpgConfig;
-import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SignatureVerifier;
import org.eclipse.jgit.util.LRUMap;
import org.eclipse.jgit.util.StringUtils;
/**
- * A {@link GpgSignatureVerifier} to verify GPG signatures using BouncyCastle.
+ * A {@link SignatureVerifier} to verify GPG signatures using BouncyCastle.
*/
public class BouncyCastleGpgSignatureVerifier
- extends AbstractGpgSignatureVerifier {
+ implements SignatureVerifier {
- 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();
- }
+ private static final String NAME = "bc"; //$NON-NLS-1$
// To support more efficient signature verification of multiple objects we
// cache public keys once found in a LRU cache.
@@ -70,7 +55,7 @@ public class BouncyCastleGpgSignatureVerifier
@Override
public String getName() {
- return "bc"; //$NON-NLS-1$
+ return NAME;
}
static PGPSignature parseSignature(InputStream in)
@@ -90,9 +75,8 @@ public class BouncyCastleGpgSignatureVerifier
}
@Override
- public SignatureVerification verify(@NonNull GpgConfig config, byte[] data,
- byte[] signatureData)
- throws IOException {
+ public SignatureVerification verify(Repository repository, GpgConfig config,
+ byte[] data, byte[] signatureData) throws IOException {
PGPSignature signature = null;
String fingerprint = null;
String signer = null;
@@ -127,14 +111,15 @@ public class BouncyCastleGpgSignatureVerifier
}
Date signatureCreatedAt = signature.getCreationTime();
if (fingerprint == null && signer == null && keyId == null) {
- return new VerificationResult(signatureCreatedAt, null, null, null,
- false, false, TrustLevel.UNKNOWN,
+ return new SignatureVerification(NAME, 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,
- signer, false, false, TrustLevel.UNKNOWN,
+ return new SignatureVerification(NAME, signatureCreatedAt,
+ signer, fingerprint, signer, false, false,
+ TrustLevel.UNKNOWN,
MessageFormat.format(BCText.get().signatureInconsistent,
keyId, fingerprint));
}
@@ -175,15 +160,16 @@ public class BouncyCastleGpgSignatureVerifier
bySigner.put(signer, NO_KEY);
}
}
- return new VerificationResult(signatureCreatedAt, signer,
- fingerprint, signer, false, false, TrustLevel.UNKNOWN,
- BCText.get().signatureNoPublicKey);
+ return new SignatureVerification(NAME, signatureCreatedAt,
+ signer, fingerprint, signer, false, false,
+ TrustLevel.UNKNOWN, BCText.get().signatureNoPublicKey);
}
if (fingerprint != null && !publicKey.isExactMatch()) {
// We did find _some_ signing key for the signer, but it doesn't
// match the given fingerprint.
- return new VerificationResult(signatureCreatedAt, signer,
- fingerprint, signer, false, false, TrustLevel.UNKNOWN,
+ return new SignatureVerification(NAME, signatureCreatedAt,
+ signer, fingerprint, signer, false, false,
+ TrustLevel.UNKNOWN,
MessageFormat.format(BCText.get().signatureNoSigningKey,
fingerprint));
}
@@ -229,8 +215,7 @@ public class BouncyCastleGpgSignatureVerifier
boolean verified = false;
try {
signature.init(
- new JcaPGPContentVerifierBuilderProvider()
- .setProvider(BouncyCastleProvider.PROVIDER_NAME),
+ new JcaPGPContentVerifierBuilderProvider(),
pubKey);
signature.update(data);
verified = signature.verify();
@@ -238,15 +223,8 @@ public class BouncyCastleGpgSignatureVerifier
throw new JGitInternalException(
BCText.get().signatureVerificationError, e);
}
- return new VerificationResult(signatureCreatedAt, signer, fingerprint, user,
- verified, expired, trust, null);
- }
-
- @Override
- public SignatureVerification verify(byte[] data, byte[] signatureData)
- throws IOException {
- throw new UnsupportedOperationException(
- "Call verify(GpgConfig, byte[], byte[]) instead."); //$NON-NLS-1$
+ return new SignatureVerification(NAME, signatureCreatedAt, signer,
+ fingerprint, user, verified, expired, trust, null);
}
private TrustLevel parseGpgTrustPacket(byte[] packet) {
@@ -282,76 +260,4 @@ public class BouncyCastleGpgSignatureVerifier
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
index ae82b758a6..566ad1bf91 100644
--- 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.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
@@ -9,20 +9,27 @@
*/
package org.eclipse.jgit.gpg.bc.internal;
-import org.eclipse.jgit.lib.GpgSignatureVerifier;
-import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.SignatureVerifier;
+import org.eclipse.jgit.lib.SignatureVerifierFactory;
/**
- * A {@link GpgSignatureVerifierFactory} that creates
- * {@link GpgSignatureVerifier} instances that verify GPG signatures using
- * BouncyCastle and that do cache public keys.
+ * A {@link SignatureVerifierFactory} that creates {@link SignatureVerifier}
+ * instances that verify GPG signatures using BouncyCastle and that do cache
+ * public keys.
*/
public final class BouncyCastleGpgSignatureVerifierFactory
- extends GpgSignatureVerifierFactory {
+ implements SignatureVerifierFactory {
@Override
- public GpgSignatureVerifier getVerifier() {
+ public GpgFormat getType() {
+ return GpgFormat.OPENPGP;
+ }
+
+ @Override
+ public SignatureVerifier create() {
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 763b7f7526..adac9b199d 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, 2021, Salesforce and others
+ * Copyright (C) 2018, 2024, 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,13 +14,11 @@ import java.io.IOException;
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
-import java.security.Security;
import java.util.Iterator;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.HashAlgorithmTags;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
@@ -30,79 +28,23 @@ import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
-import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.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.lib.Repository;
+import org.eclipse.jgit.lib.Signer;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.util.StringUtils;
/**
* GPG Signer using the BouncyCastle library.
*/
-public class BouncyCastleGpgSigner extends GpgSigner
- implements GpgObjectSigner {
-
- private static void registerBouncyCastleProviderIfNecessary() {
- if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
- Security.addProvider(new BouncyCastleProvider());
- }
- }
-
- /**
- * Create a new instance.
- * <p>
- * The BounceCastleProvider will be registered if necessary.
- * </p>
- */
- public BouncyCastleGpgSigner() {
- registerBouncyCastleProviderIfNecessary();
- }
-
- @Override
- public boolean canLocateSigningKey(@Nullable String gpgSigningKey,
- PersonIdent committer, CredentialsProvider credentialsProvider)
- throws CanceledException {
- try {
- 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 (CanceledException e) {
- throw e;
- } catch (Exception e) {
- return false;
- }
- }
+public class BouncyCastleGpgSigner implements Signer {
private BouncyCastleGpgKey locateSigningKey(@Nullable String gpgSigningKey,
PersonIdent committer,
@@ -121,38 +63,24 @@ public class BouncyCastleGpgSigner extends GpgSigner
}
@Override
- 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);
+ public GpgSignature sign(Repository repository, GpgConfig config,
+ byte[] data, PersonIdent committer, String signingKey,
+ CredentialsProvider credentialsProvider) throws CanceledException,
+ IOException, UnsupportedSigningFormatException {
+ String gpgSigningKey = signingKey;
+ if (gpgSigningKey == null) {
+ gpgSigningKey = config.getSigningKey();
}
try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
credentialsProvider)) {
BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
- committer,
- passphrasePrompt);
+ committer, passphrasePrompt);
PGPSecretKey secretKey = gpgKey.getSecretKey();
if (secretKey == null) {
throw new JGitInternalException(
BCText.get().unableToSignCommitNoSecretKey);
}
- JcePBESecretKeyDecryptorBuilder decryptorBuilder = new JcePBESecretKeyDecryptorBuilder()
- .setProvider(BouncyCastleProvider.PROVIDER_NAME);
+ JcePBESecretKeyDecryptorBuilder decryptorBuilder = new JcePBESecretKeyDecryptorBuilder();
PGPPrivateKey privateKey = null;
if (!passphrasePrompt.hasPassphrase()) {
// Either the key is not encrypted, or it was read from the
@@ -177,8 +105,8 @@ public class BouncyCastleGpgSigner extends GpgSigner
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
new JcaPGPContentSignerBuilder(
publicKey.getAlgorithm(),
- HashAlgorithmTags.SHA256).setProvider(
- BouncyCastleProvider.PROVIDER_NAME));
+ HashAlgorithmTags.SHA256),
+ publicKey);
signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, privateKey);
PGPSignatureSubpacketGenerator subpackets = new PGPSignatureSubpacketGenerator();
subpackets.setIssuerFingerprint(false, publicKey);
@@ -202,16 +130,36 @@ public class BouncyCastleGpgSigner extends GpgSigner
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (BCPGOutputStream out = new BCPGOutputStream(
new ArmoredOutputStream(buffer))) {
- signatureGenerator.update(object.build());
+ signatureGenerator.update(data);
signatureGenerator.generate().encode(out);
}
- object.setGpgSignature(new GpgSignature(buffer.toByteArray()));
- } catch (PGPException | IOException | NoSuchAlgorithmException
+ return new GpgSignature(buffer.toByteArray());
+ } catch (PGPException | NoSuchAlgorithmException
| NoSuchProviderException | URISyntaxException e) {
throw new JGitInternalException(e.getMessage(), e);
}
}
+ @Override
+ public boolean canLocateSigningKey(Repository repository, GpgConfig config,
+ PersonIdent committer, String signingKey,
+ CredentialsProvider credentialsProvider) throws CanceledException {
+ String gpgSigningKey = signingKey;
+ if (gpgSigningKey == null) {
+ gpgSigningKey = config.getSigningKey();
+ }
+ try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
+ credentialsProvider)) {
+ BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
+ committer, passphrasePrompt);
+ return gpgKey != null;
+ } catch (CanceledException e) {
+ throw e;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
static String extractSignerId(String pgpUserId) {
int from = pgpUserId.indexOf('<');
if (from >= 0) {
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignerFactory.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignerFactory.java
new file mode 100644
index 0000000000..92ab65d7e4
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignerFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal;
+
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.Signer;
+import org.eclipse.jgit.lib.SignerFactory;
+
+/**
+ * Factory for creating a {@link Signer} for OPENPGP signatures based on Bouncy
+ * Castle.
+ */
+public final class BouncyCastleGpgSignerFactory implements SignerFactory {
+
+ @Override
+ public GpgFormat getType() {
+ return GpgFormat.OPENPGP;
+ }
+
+ @Override
+ public Signer create() {
+ return new BouncyCastleGpgSigner();
+ }
+}
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
deleted file mode 100644
index 3924d68596..0000000000
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/OCBPBEProtectionRemoverFactory.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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 {
- // errorprone: "Dynamically constructed transformation
- // strings are also flagged, as they may conceal an instance
- // of ECB mode."
- @SuppressWarnings("InsecureCryptoUsage")
- 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
deleted file mode 100644
index fd030ee032..0000000000
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SExprParser.java
+++ /dev/null
@@ -1,859 +0,0 @@
-/*
- * 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.ASN1ObjectIdentifier;
-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)</li>
- * <li>handle secret keys using AES/OCB as encryption (those don't have a
- * hash)</li>
- * <li>fix EC parsing to account for "flags" sub-list present for ed25519 and
- * curve25519</li>
- * <li>add support for ed25519 OIDs unknown to BouncyCastle</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
- * if an IO error occurred
- * @throws PGPException
- * if some PGP error occurred
- */
- 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());
- // JGit: c.f. https://github.com/bcgit/bc-java/issues/1590.
- // There may be a flags sub-list here for ed25519 or curve25519.
- if (type.equals("flags")) {
- SXprUtils.readString(inputStream, inputStream.read());
- SXprUtils.skipCloseParenthesis(inputStream);
- 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());
- }
-
- // JGit: BC doesn't know Ed25519 curve name.
- ASN1ObjectIdentifier curveOid = ECNamedCurveTable
- .getOID(curveName);
- if (curveOid == null) {
- curveOid = ObjectIds.getByName(curveName);
- }
- ECPublicBCPGKey basePubKey = new ECDSAPublicBCPGKey(
- curveOid,
- new BigInteger(1, qVal));
- ECPublicBCPGKey assocPubKey = (ECPublicBCPGKey) pubKey
- .getPublicKeyPacket().getKey();
- if (!ObjectIds.match(basePubKey.getCurveOID(),
- 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
- * if an IO error occurred
- * @throws PGPException
- * if a PGP error occurred
- */
- 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());
- // JGit: c.f. https://github.com/bcgit/bc-java/issues/1590.
- // There may be a flags sub-list here for ed25519 or curve25519.
- if (type.equals("flags")) {
- SXprUtils.readString(inputStream, inputStream.read());
- SXprUtils.skipCloseParenthesis(inputStream);
- 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
deleted file mode 100644
index 220aa285ff..0000000000
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SXprUtils.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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
index a659d38fd3..a56e418bf4 100644
--- 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.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
@@ -9,34 +9,36 @@
*/
package org.eclipse.jgit.gpg.bc.internal.keys;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
+import java.io.BufferedInputStream;
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.bcpg.ECPublicBCPGKey;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.gpg.PGPSecretKeyParser;
+import org.bouncycastle.gpg.SExprParser;
+import org.bouncycastle.openpgp.OpenedPGPKeyData;
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.JcaKeyFingerprintCalculator;
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 {
+ // Maximum nesting depth of sub-lists in an S-Expression for a secret key.
+ private static final int MAX_SEXPR_NESTING = 20;
+
private SecretKeys() {
// No instantiation.
}
@@ -64,12 +66,6 @@ public final class SecretKeys {
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.
*
@@ -99,500 +95,59 @@ public final class SecretKeys {
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();
+ OpenedPGPKeyData data;
+ try (InputStream keyIn = new BufferedInputStream(in)) {
+ data = PGPSecretKeyParser.parse(keyIn, MAX_SEXPR_NESTING);
}
- }
-
- /**
- * 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
- * if object stream is corrupt
- */
- 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)));
- }
+ PBEProtectionRemoverFactory decryptor = null;
+ if (isProtected(data)) {
+ decryptor = new JcePBEProtectionRemoverFactory(
+ passphraseSupplier.getPassphrase(), calculatorProvider);
}
- 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();
+ switch (publicKey.getAlgorithm()) {
+ case PublicKeyAlgorithmTags.EDDSA_LEGACY:
+ case PublicKeyAlgorithmTags.Ed25519:
+ // If we let Bouncy Castle check whether the secret key matches the
+ // given public key it may get into trouble in some cases with
+ // ed25519 keys. It appears that we may end up with secret keys
+ // using the official RFC 8410 OID for ed25519, "1.3.101.112", while
+ // the public key passed in may have a non-standard OpenPGP-specific
+ // OID "1.3.6.1.4.1.11591.15.1", or vice versa. Bouncy Castle then
+ // throws an exception because of the different OIDs.
+ //
+ // The work-around is to just read the secret key, and double-check
+ // later that the OIDs are compatible and the curve points match.
+ PGPSecretKey secret = data.getKeyData(null, calculatorProvider,
+ decryptor, new JcaKeyFingerprintCalculator(),
+ MAX_SEXPR_NESTING);
+ PGPPublicKey pubKeyRead = secret.getPublicKey();
+ int algoRead = pubKeyRead.getAlgorithm();
+ if (algoRead != PublicKeyAlgorithmTags.EDDSA_LEGACY
+ && algoRead != PublicKeyAlgorithmTags.Ed25519) {
+ throw new PGPException(BCText.get().keyAlgorithmMismatch);
}
- 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;
+ ECPublicBCPGKey ec1 = (ECPublicBCPGKey) publicKey
+ .getPublicKeyPacket().getKey();
+ ECPublicBCPGKey ec2 = (ECPublicBCPGKey) pubKeyRead
+ .getPublicKeyPacket().getKey();
+ if (!ObjectIds.match(ec1.getCurveOID(), ec2.getCurveOID())
+ || !ec1.getEncodedPoint().equals(ec2.getEncodedPoint())) {
+ throw new PGPException(
+ MessageFormat.format(BCText.get().keyMismatch,
+ ec1.getCurveOID(), ec1.getEncodedPoint(),
+ ec2.getCurveOID(), ec2.getEncodedPoint()));
}
- 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;
+ return secret;
default:
- if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
- || (ch >= '0' && ch <= '9')) {
- return true;
- }
- return false;
+ // For other key types let Bouncy Castle do the check.
+ return data.getKeyData(publicKey, calculatorProvider, decryptor,
+ null, MAX_SEXPR_NESTING);
}
}
- private static boolean isHex(int ch) {
- return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')
- || (ch >= 'a' && ch <= 'f');
+ private static boolean isProtected(OpenedPGPKeyData data) {
+ return SExprParser.ProtectionFormatTypeTags.PROTECTED_PRIVATE_KEY == SExprParser
+ .getProtectionType(data.getKeyExpression().getString(0));
}
- 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 c1ec7ae7c8..244b20a0f4 100644
--- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.http.apache
Bundle-SymbolicName: org.eclipse.jgit.http.apache
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
@@ -26,11 +26,11 @@ Import-Package: javax.net.ssl,
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="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="7.0.0";
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="7.3.0";
uses:="org.apache.http.client,
org.eclipse.jgit.transport.http,
org.apache.http.entity,
diff --git a/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
index d29d3879d5..86adeb6820 100644
--- a/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.http.apache - Sources
Bundle-SymbolicName: org.eclipse.jgit.http.apache.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index 51a79d61ef..0c7c5ffc7d 100644
--- a/org.eclipse.jgit.http.apache/pom.xml
+++ b/org.eclipse.jgit.http.apache/pom.xml
@@ -15,7 +15,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.http.apache</artifactId>
diff --git a/org.eclipse.jgit.http.server/BUILD b/org.eclipse.jgit.http.server/BUILD
index f8aa44db94..a0dae488b8 100644
--- a/org.eclipse.jgit.http.server/BUILD
+++ b/org.eclipse.jgit.http.server/BUILD
@@ -2,11 +2,16 @@ load("@rules_java//java:defs.bzl", "java_library")
package(default_visibility = ["//visibility:public"])
+filegroup(
+ name = "jgit-servlet-resources",
+ srcs = glob(["resources/**"]),
+)
+
java_library(
name = "jgit-servlet",
srcs = glob(["src/**/*.java"]),
resource_strip_prefix = "org.eclipse.jgit.http.server/resources",
- resources = glob(["resources/**"]),
+ resources = [":jgit-servlet-resources"],
deps = [
"//lib:servlet-api",
# We want these deps to be provided_deps
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index 124129d73d..71c471da1a 100644
--- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.http.server
Bundle-SymbolicName: org.eclipse.jgit.http.server
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.http.server;version="7.0.0",
- org.eclipse.jgit.http.server.glue;version="7.0.0";
+Export-Package: org.eclipse.jgit.http.server;version="7.3.0",
+ org.eclipse.jgit.http.server.glue;version="7.3.0";
uses:="jakarta.servlet,
jakarta.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="7.0.0";
+ org.eclipse.jgit.http.server.resolver;version="7.3.0";
uses:="jakarta.servlet.http
org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.lib,
@@ -19,14 +19,14 @@ Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: jakarta.servlet;version="[6.0.0,7.0.0)",
jakarta.servlet.http;version="[6.0.0,7.0.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.parser;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.resolver;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)"
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)"
diff --git a/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF
index bbb29cd846..67a14cfbf5 100644
--- a/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.http.server - Sources
Bundle-SymbolicName: org.eclipse.jgit.http.server.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index 7645dd1418..b34ca2fe10 100644
--- a/org.eclipse.jgit.http.server/pom.xml
+++ b/org.eclipse.jgit.http.server/pom.xml
@@ -19,7 +19,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.http.server</artifactId>
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
index d2f0cd2731..bf3da4b5d0 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
@@ -255,9 +255,11 @@ public class GitSmartHttpTools {
private static void send(HttpServletRequest req, HttpServletResponse res,
String type, byte[] buf, int httpStatus) throws IOException {
ServletUtils.consumeRequestBody(req);
- res.setStatus(httpStatus);
- res.setContentType(type);
- res.setContentLength(buf.length);
+ if (!res.isCommitted()) {
+ res.setStatus(httpStatus);
+ res.setContentType(type);
+ res.setContentLength(buf.length);
+ }
try (OutputStream os = res.getOutputStream()) {
os.write(buf);
}
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index edce2d8d27..518afb3286 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.http.test
Bundle-SymbolicName: org.eclipse.jgit.http.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -29,26 +29,26 @@ Import-Package: jakarta.servlet;version="[6.0.0,7.0.0)",
org.eclipse.jetty.util.component;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.security;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.thread;version="[12.0.0,13.0.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.http.server;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.http.server.glue;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.http.server.resolver;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http.apache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.resolver;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.http.server;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.http.server.glue;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.http.server.resolver;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.rules;version="[4.13,5.0.0)",
org.junit.runner;version="[4.13,5.0.0)",
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
index 01ee7824ca..8ed3017c9c 100644
--- a/org.eclipse.jgit.http.test/pom.xml
+++ b/org.eclipse.jgit.http.test/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.http.test</artifactId>
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 850e895790..b0d17adb3a 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
@@ -1728,7 +1728,8 @@ public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase {
assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
fsck(remoteRepository, Q);
- final ReflogReader log = remoteRepository.getReflogReader(dstName);
+ final ReflogReader log = remoteRepository.getRefDatabase()
+ .getReflogReader(dstName);
assertNotNull("has log for " + dstName, log);
final ReflogEntry last = log.getLastEntry();
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index 3e983511d5..e3a6e264cb 100644
--- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.junit.http
Bundle-SymbolicName: org.eclipse.jgit.junit.http
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
@@ -22,17 +22,17 @@ Import-Package: jakarta.servlet;version="[6.0.0,7.0.0)",
org.eclipse.jetty.util.component;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.security;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.ssl;version="[12.0.0,13.0.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.http.server;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.resolver;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.http.server;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.slf4j.helpers;version="[1.7.0,3.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="7.0.0";
+Export-Package: org.eclipse.jgit.junit.http;version="7.3.0";
uses:="org.eclipse.jgit.transport,
jakarta.servlet,
jakarta.servlet.http,
diff --git a/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
index cfeb426385..855f210254 100644
--- a/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.junit.http - Sources
Bundle-SymbolicName: org.eclipse.jgit.junit.http.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index 67c82652cb..2947f21cfe 100644
--- a/org.eclipse.jgit.junit.http/pom.xml
+++ b/org.eclipse.jgit.junit.http/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
index 424bac1043..30c359b785 100644
--- a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
@@ -3,46 +3,46 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.junit.ssh
Bundle-SymbolicName: org.eclipse.jgit.junit.ssh
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Import-Package: org.apache.sshd.common;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.config.keys;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.file.virtualfs;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.helpers;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.io;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.kex;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.keyprovider;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.session;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.signature;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.buffer;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.logging;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.security;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.threads;version="[2.12.0,2.13.0)",
- org.apache.sshd.core;version="[2.12.0,2.13.0)",
- org.apache.sshd.server;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.auth;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.auth.gss;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.auth.keyboard;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.auth.password;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.command;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.session;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.shell;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.subsystem;version="[2.12.0,2.13.0)",
- org.apache.sshd.sftp;version="[2.12.0,2.13.0)",
- org.apache.sshd.sftp.server;version="[2.12.0,2.13.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+Import-Package: org.apache.sshd.common;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.config.keys;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.file.virtualfs;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.helpers;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.io;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.kex;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.keyprovider;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.session;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.signature;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.buffer;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.logging;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.security;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.threads;version="[2.15.0,2.16.0)",
+ org.apache.sshd.core;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.auth;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.auth.gss;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.auth.keyboard;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.auth.password;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.command;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.session;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.shell;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.subsystem;version="[2.15.0,2.16.0)",
+ org.apache.sshd.sftp;version="[2.15.0,2.16.0)",
+ org.apache.sshd.sftp.server;version="[2.15.0,2.16.0)",
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.experimental.theories;version="[4.13,5.0.0)",
org.slf4j;version="[1.7.0,3.0.0)"
-Export-Package: org.eclipse.jgit.junit.ssh;version="7.0.0"
+Export-Package: org.eclipse.jgit.junit.ssh;version="7.3.0"
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
index a7ca6336b8..27d1737414 100644
--- a/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.junit.ssh - Sources
Bundle-SymbolicName: org.eclipse.jgit.junit.ssh.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml
index 2a885f204e..cbdfa319a9 100644
--- a/org.eclipse.jgit.junit.ssh/pom.xml
+++ b/org.eclipse.jgit.junit.ssh/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.junit.ssh</artifactId>
diff --git a/org.eclipse.jgit.junit/.settings/.api_filters b/org.eclipse.jgit.junit/.settings/.api_filters
new file mode 100644
index 0000000000..27815301c2
--- /dev/null
+++ b/org.eclipse.jgit.junit/.settings/.api_filters
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.jgit.junit" version="2">
+ <resource path="src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java" type="org.eclipse.jgit.junit.LocalDiskRepositoryTestCase">
+ <filter id="336658481">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.junit.LocalDiskRepositoryTestCase"/>
+ <message_argument value="testRoot"/>
+ </message_arguments>
+ </filter>
+ </resource>
+</component>
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index af1cbf256a..5f0546efff 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -3,36 +3,36 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.junit
Bundle-SymbolicName: org.eclipse.jgit.junit
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Import-Package: org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.dircache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.pack;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.merge;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="7.0.0",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.io;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.time;version="[7.0.0,7.1.0)",
+Import-Package: org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.dircache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.merge;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="7.3.0",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.io;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.time;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.rules;version="[4.13,5.0.0)",
org.junit.runner;version="[4.13,5.0.0)",
org.junit.runners;version="[4.13,5.0.0)",
org.junit.runners.model;version="[4.13,5.0.0)",
org.slf4j;version="[1.7.0,3.0.0)"
-Export-Package: org.eclipse.jgit.junit;version="7.0.0";
+Export-Package: org.eclipse.jgit.junit;version="7.3.0";
uses:="org.eclipse.jgit.dircache,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
@@ -45,4 +45,4 @@ Export-Package: org.eclipse.jgit.junit;version="7.0.0";
org.junit.runners.model,
org.junit.runner,
org.eclipse.jgit.util.time",
- org.eclipse.jgit.junit.time;version="7.0.0";uses:="org.eclipse.jgit.util.time"
+ org.eclipse.jgit.junit.time;version="7.3.0";uses:="org.eclipse.jgit.util.time"
diff --git a/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
index 149ad00121..4e0108a454 100644
--- a/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.junit - Sources
Bundle-SymbolicName: org.eclipse.jgit.junit.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index c33cb29b66..dbb8b06143 100644
--- a/org.eclipse.jgit.junit/pom.xml
+++ b/org.eclipse.jgit.junit/pom.xml
@@ -19,7 +19,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/FakeIndexFactory.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/FakeIndexFactory.java
new file mode 100644
index 0000000000..eb23bec584
--- /dev/null
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/FakeIndexFactory.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * 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;
+
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+import static java.util.stream.Collectors.toUnmodifiableList;
+
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.file.PackIndex.EntriesIterator;
+import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Create indexes with predefined data
+ *
+ * @since 7.2
+ */
+public class FakeIndexFactory {
+
+ /**
+ * An object for the fake index
+ *
+ * @param name
+ * a sha1
+ * @param offset
+ * the (fake) position of the object in the pack
+ */
+ public record IndexObject(String name, long offset) {
+ /**
+ * Name (sha1) as an objectId
+ *
+ * @return name (a sha1) as an objectId.
+ */
+ public ObjectId getObjectId() {
+ return ObjectId.fromString(name);
+ }
+ }
+
+ /**
+ * Return an index populated with these objects
+ *
+ * @param objs
+ * objects to be indexed
+ * @return a PackIndex implementation
+ */
+ public static PackIndex indexOf(List<IndexObject> objs) {
+ return new FakePackIndex(objs);
+ }
+
+ /**
+ * Return a reverse pack index with these objects
+ *
+ * @param objs
+ * objects to be indexed
+ * @return a PackReverseIndex implementation
+ */
+ public static PackReverseIndex reverseIndexOf(List<IndexObject> objs) {
+ return new FakeReverseIndex(objs);
+ }
+
+ private FakeIndexFactory() {
+ }
+
+ private static class FakePackIndex implements PackIndex {
+ private static final Comparator<IndexObject> SHA1_COMPARATOR = (o1,
+ o2) -> String.CASE_INSENSITIVE_ORDER.compare(o1.name(),
+ o2.name());
+
+ private final Map<String, IndexObject> idx;
+
+ private final List<IndexObject> sha1Ordered;
+
+ private final long offset64count;
+
+ FakePackIndex(List<IndexObject> objs) {
+ sha1Ordered = objs.stream().sorted(SHA1_COMPARATOR)
+ .collect(toUnmodifiableList());
+ idx = objs.stream().collect(toMap(IndexObject::name, identity()));
+ offset64count = objs.stream()
+ .filter(o -> o.offset > Integer.MAX_VALUE).count();
+ }
+
+ @Override
+ public Iterator<MutableEntry> iterator() {
+ return new FakeEntriesIterator(sha1Ordered);
+ }
+
+ @Override
+ public long getObjectCount() {
+ return sha1Ordered.size();
+ }
+
+ @Override
+ public long getOffset64Count() {
+ return offset64count;
+ }
+
+ @Override
+ public ObjectId getObjectId(long nthPosition) {
+ return ObjectId
+ .fromString(sha1Ordered.get((int) nthPosition).name());
+ }
+
+ @Override
+ public long getOffset(long nthPosition) {
+ return sha1Ordered.get((int) nthPosition).offset();
+ }
+
+ @Override
+ public long findOffset(AnyObjectId objId) {
+ IndexObject o = idx.get(objId.name());
+ if (o == null) {
+ return -1;
+ }
+ return o.offset();
+ }
+
+ @Override
+ public int findPosition(AnyObjectId objId) {
+ IndexObject o = idx.get(objId.name());
+ if (o == null) {
+ return -1;
+ }
+ return sha1Ordered.indexOf(o);
+ }
+
+ @Override
+ public long findCRC32(AnyObjectId objId) throws MissingObjectException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasCRC32Support() {
+ return false;
+ }
+
+ @Override
+ public void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+ int matchLimit) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] getChecksum() {
+ return new byte[0];
+ }
+ }
+
+ private static class FakeReverseIndex implements PackReverseIndex {
+ private static final Comparator<IndexObject> OFFSET_COMPARATOR = Comparator
+ .comparingLong(IndexObject::offset);
+
+ private final List<IndexObject> byOffset;
+
+ private final Map<Long, IndexObject> ridx;
+
+ FakeReverseIndex(List<IndexObject> objs) {
+ byOffset = objs.stream().sorted(OFFSET_COMPARATOR)
+ .collect(toUnmodifiableList());
+ ridx = byOffset.stream()
+ .collect(toMap(IndexObject::offset, identity()));
+ }
+
+ @Override
+ public void verifyPackChecksum(String packFilePath) {
+ // Do nothing
+ }
+
+ @Override
+ public ObjectId findObject(long offset) {
+ IndexObject indexObject = ridx.get(offset);
+ if (indexObject == null) {
+ return null;
+ }
+ return ObjectId.fromString(indexObject.name());
+ }
+
+ @Override
+ public long findNextOffset(long offset, long maxOffset)
+ throws CorruptObjectException {
+ IndexObject o = ridx.get(offset);
+ if (o == null) {
+ throw new CorruptObjectException("Invalid offset"); //$NON-NLS-1$
+ }
+ int pos = byOffset.indexOf(o);
+ if (pos == byOffset.size() - 1) {
+ return maxOffset;
+ }
+ return byOffset.get(pos + 1).offset();
+ }
+
+ @Override
+ public int findPosition(long offset) {
+ IndexObject indexObject = ridx.get(offset);
+ return byOffset.indexOf(indexObject);
+ }
+
+ @Override
+ public ObjectId findObjectByPosition(int nthPosition) {
+ return byOffset.get(nthPosition).getObjectId();
+ }
+ }
+
+ private static class FakeEntriesIterator extends EntriesIterator {
+
+ private static final byte[] buffer = new byte[Constants.OBJECT_ID_LENGTH];
+
+ private final Iterator<IndexObject> it;
+
+ FakeEntriesIterator(List<IndexObject> objs) {
+ super(objs.size());
+ it = objs.iterator();
+ }
+
+ @Override
+ protected void readNext() {
+ IndexObject next = it.next();
+ next.getObjectId().copyRawTo(buffer, 0);
+ setIdBuffer(buffer, 0);
+ setOffset(next.offset());
+ }
+ }
+}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
index 407290a399..0d20f6488a 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
@@ -20,19 +20,19 @@ import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.time.Instant;
+import java.time.ZoneId;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
-import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.internal.util.ShutdownHook;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@@ -47,6 +47,7 @@ import org.eclipse.jgit.util.SystemReader;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestName;
/**
@@ -84,6 +85,16 @@ public abstract class LocalDiskRepositoryTestCase {
protected MockSystemReader mockSystemReader;
private final Set<Repository> toClose = new HashSet<>();
+
+ /**
+ * Temporary test root directory for files created by tests.
+ * @since 7.2
+ */
+ @Rule
+ public TemporaryFolder testRoot = new TemporaryFolder();
+
+ Random rand = new Random();
+
private File tmp;
private File homeDir;
@@ -114,11 +125,8 @@ public abstract class LocalDiskRepositoryTestCase {
*/
@Before
public void setUp() throws Exception {
- tmp = File.createTempFile("jgit_" + getTestName() + '_', "_tmp");
- Cleanup.deleteOnShutdown(tmp);
- if (!tmp.delete() || !tmp.mkdir()) {
- throw new IOException("Cannot create " + tmp);
- }
+ tmp = testRoot.newFolder(getTestName() + rand.nextInt());
+
mockSystemReader = new MockSystemReader();
SystemReader.setInstance(mockSystemReader);
@@ -219,12 +227,6 @@ public abstract class LocalDiskRepositoryTestCase {
System.gc();
}
FS.DETECTED.setUserHome(homeDir);
- if (tmp != null) {
- recursiveDelete(tmp, false, true);
- }
- if (tmp != null && !tmp.exists()) {
- Cleanup.removed(tmp);
- }
SystemReader.setInstance(null);
}
@@ -233,8 +235,8 @@ public abstract class LocalDiskRepositoryTestCase {
*/
protected void tick() {
mockSystemReader.tick(5 * 60);
- final long now = mockSystemReader.getCurrentTime();
- final int tz = mockSystemReader.getTimezone(now);
+ Instant now = mockSystemReader.now();
+ ZoneId tz = mockSystemReader.getTimeZoneId();
author = new PersonIdent(author, now, tz);
committer = new PersonIdent(committer, now, tz);
@@ -623,41 +625,4 @@ public abstract class LocalDiskRepositoryTestCase {
private static HashMap<String, String> cloneEnv() {
return new HashMap<>(System.getenv());
}
-
- private static final class Cleanup {
- private static final Cleanup INSTANCE = new Cleanup();
-
- static {
- ShutdownHook.INSTANCE.register(() -> INSTANCE.onShutdown());
- }
-
- private final Set<File> toDelete = ConcurrentHashMap.newKeySet();
-
- private Cleanup() {
- // empty
- }
-
- static void deleteOnShutdown(File tmp) {
- INSTANCE.toDelete.add(tmp);
- }
-
- static void removed(File tmp) {
- INSTANCE.toDelete.remove(tmp);
- }
-
- private void onShutdown() {
- // On windows accidentally open files or memory
- // mapped regions may prevent files from being deleted.
- // Suggesting a GC increases the likelihood that our
- // test repositories actually get removed after the
- // tests, even in the case of failure.
- System.gc();
- synchronized (this) {
- boolean silent = false;
- boolean failOnError = false;
- for (File tmp : toDelete)
- recursiveDelete(tmp, silent, failOnError);
- }
- }
- }
}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
index 419fdb1966..38f0d0b2cb 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
@@ -18,6 +18,8 @@ import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Duration;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@@ -242,6 +244,11 @@ public class MockSystemReader extends SystemReader {
}
@Override
+ public ZoneId getTimeZoneId() {
+ return ZoneOffset.ofHoursMinutes(-3, -30);
+ }
+
+ @Override
public Locale getLocale() {
return Locale.US;
}
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 c8c56b21b8..2a482df04a 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
@@ -44,7 +44,7 @@ public class SeparateClassloaderTestRunner extends BlockJUnit4ClassRunner {
try {
String pathSeparator = System.getProperty("path.separator");
String[] classPathEntries = System.getProperty("java.class.path")
- .split(pathSeparator);
+ .split(pathSeparator, -1);
URL[] urls = new URL[classPathEntries.length];
for (int i = 0; i < classPathEntries.length; i++) {
urls[i] = Paths.get(classPathEntries[i]).toUri().toURL();
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 a2e0a571eb..2d00a850e5 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
@@ -21,6 +21,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.time.Instant;
+import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -30,6 +31,7 @@ import java.util.List;
import java.util.Set;
import java.util.TimeZone;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
@@ -157,8 +159,8 @@ public class TestRepository<R extends Repository> implements AutoCloseable {
this.pool = rw;
this.inserter = db.newObjectInserter();
this.mockSystemReader = reader;
- long now = mockSystemReader.getCurrentTime();
- int tz = mockSystemReader.getTimezone(now);
+ Instant now = mockSystemReader.now();
+ ZoneId tz = mockSystemReader.getTimeZoneAt(now);
defaultAuthor = new PersonIdent(AUTHOR, AUTHOR_EMAIL, now, tz);
defaultCommitter = new PersonIdent(COMMITTER, COMMITTER_EMAIL, now, tz);
}
@@ -197,7 +199,9 @@ public class TestRepository<R extends Repository> implements AutoCloseable {
*
* @return current date.
* @since 4.2
+ * @deprecated Use {@link #getInstant()} instead.
*/
+ @Deprecated(since = "7.2")
public Date getDate() {
return new Date(mockSystemReader.getCurrentTime());
}
@@ -209,18 +213,31 @@ public class TestRepository<R extends Repository> implements AutoCloseable {
* @since 6.8
*/
public Instant getInstant() {
- return Instant.ofEpochMilli(mockSystemReader.getCurrentTime());
+ return mockSystemReader.now();
}
/**
* Get timezone
*
* @return timezone used for default identities.
+ * @deprecated Use {@link #getTimeZoneId()} instead.
*/
+ @Deprecated(since = "7.2")
public TimeZone getTimeZone() {
return mockSystemReader.getTimeZone();
}
+
+ /**
+ * Get timezone
+ *
+ * @return timezone used for default identities.
+ * @since 7.2
+ */
+ public ZoneId getTimeZoneId() {
+ return mockSystemReader.getTimeZoneId();
+ }
+
/**
* Adjust the current time that will used by the next commit.
*
@@ -232,14 +249,14 @@ public class TestRepository<R extends Repository> implements AutoCloseable {
}
/**
- * Set the author and committer using {@link #getDate()}.
+ * Set the author and committer using {@link #getInstant()}.
*
* @param c
* the commit builder to store.
*/
public void setAuthorAndCommitter(org.eclipse.jgit.lib.CommitBuilder c) {
- c.setAuthor(new PersonIdent(defaultAuthor, getDate()));
- c.setCommitter(new PersonIdent(defaultCommitter, getDate()));
+ c.setAuthor(new PersonIdent(defaultAuthor, getInstant()));
+ c.setCommitter(new PersonIdent(defaultCommitter, getInstant()));
}
/**
@@ -487,8 +504,8 @@ public class TestRepository<R extends Repository> implements AutoCloseable {
c = new org.eclipse.jgit.lib.CommitBuilder();
c.setTreeId(tree);
c.setParentIds(parents);
- c.setAuthor(new PersonIdent(defaultAuthor, getDate()));
- c.setCommitter(new PersonIdent(defaultCommitter, getDate()));
+ c.setAuthor(new PersonIdent(defaultAuthor, getInstant()));
+ c.setCommitter(new PersonIdent(defaultCommitter, getInstant()));
c.setMessage("");
ObjectId id;
try (ObjectInserter ins = inserter) {
@@ -528,7 +545,7 @@ public class TestRepository<R extends Repository> implements AutoCloseable {
final TagBuilder t = new TagBuilder();
t.setObjectId(dst);
t.setTag(name);
- t.setTagger(new PersonIdent(defaultCommitter, getDate()));
+ t.setTagger(new PersonIdent(defaultCommitter, getInstant()));
t.setMessage("");
ObjectId id;
try (ObjectInserter ins = inserter) {
@@ -797,7 +814,7 @@ public class TestRepository<R extends Repository> implements AutoCloseable {
b.setParentId(head);
b.setTreeId(merger.getResultTreeId());
b.setAuthor(commit.getAuthorIdent());
- b.setCommitter(new PersonIdent(defaultCommitter, getDate()));
+ b.setCommitter(new PersonIdent(defaultCommitter, getInstant()));
b.setMessage(commit.getFullMessage());
ObjectId result;
try (ObjectInserter ins = inserter) {
@@ -1019,7 +1036,8 @@ public class TestRepository<R extends Repository> implements AutoCloseable {
private static void prunePacked(ObjectDirectory odb) throws IOException {
for (Pack p : odb.getPacks()) {
for (MutableEntry e : p)
- FileUtils.delete(odb.fileFor(e.toObjectId()));
+ FileUtils.delete(odb.fileFor(e.toObjectId()),
+ FileUtils.SKIP_MISSING);
}
}
@@ -1144,15 +1162,18 @@ public class TestRepository<R extends Repository> implements AutoCloseable {
}
/**
- * set parent commit
+ * Set parent commit
*
* @param p
- * parent commit
+ * parent commit, can be {@code null}
* @return this commit builder
* @throws Exception
* if an error occurred
*/
- public CommitBuilder parent(RevCommit p) throws Exception {
+ public CommitBuilder parent(@Nullable RevCommit p) throws Exception {
+ if (p == null) {
+ return this;
+ }
if (parents.isEmpty()) {
DirCacheBuilder b = tree.builder();
parseBody(p);
@@ -1403,7 +1424,7 @@ public class TestRepository<R extends Repository> implements AutoCloseable {
c.setAuthor(author);
if (committer != null) {
if (updateCommitterTime)
- committer = new PersonIdent(committer, getDate());
+ committer = new PersonIdent(committer, getInstant());
c.setCommitter(committer);
}
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 8e41ee3d28..8feb8ef777 100644
--- a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.lfs.server.test
Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -26,24 +26,24 @@ Import-Package: jakarta.servlet;version="[6.0.0,7.0.0)",
org.eclipse.jetty.util.component;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.security;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.thread;version="[12.0.0,13.0.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.server;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.server.fs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.test;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.server;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.test;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.hamcrest.core;version="[1.1.0,3.0.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.rules;version="[4.13,5.0.0)",
diff --git a/org.eclipse.jgit.lfs.server.test/pom.xml b/org.eclipse.jgit.lfs.server.test/pom.xml
index 49c1023ef0..176f4af3f1 100644
--- a/org.eclipse.jgit.lfs.server.test/pom.xml
+++ b/org.eclipse.jgit.lfs.server.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
index a59c82ed1a..ed8cfff9be 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
@@ -3,19 +3,19 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.lfs.server
Bundle-SymbolicName: org.eclipse.jgit.lfs.server
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.lfs.server;version="7.0.0";
+Export-Package: org.eclipse.jgit.lfs.server;version="7.3.0";
uses:="jakarta.servlet.http,
org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="7.0.0";
+ org.eclipse.jgit.lfs.server.fs;version="7.3.0";
uses:="jakarta.servlet,
jakarta.servlet.http,
org.eclipse.jgit.lfs.server,
org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.internal;version="7.0.0";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="7.0.0";
+ org.eclipse.jgit.lfs.server.internal;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="7.3.0";
uses:="org.eclipse.jgit.lfs.server,
org.eclipse.jgit.lfs.lib"
Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -24,15 +24,15 @@ Import-Package: com.google.gson;version="[2.8.0,3.0.0)",
jakarta.servlet.annotation;version="[6.0.0,7.0.0)",
jakarta.servlet.http;version="[6.0.0,7.0.0)",
org.apache.http;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http.apache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.slf4j;version="[1.7.0,3.0.0)"
diff --git a/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF
index 2cf86bc942..7e26500740 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.lfs.server - Sources
Bundle-SymbolicName: org.eclipse.jgit.lfs.server.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml
index befaa367de..ebb815fd9b 100644
--- a/org.eclipse.jgit.lfs.server/pom.xml
+++ b/org.eclipse.jgit.lfs.server/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs.server</artifactId>
diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
index 96c8953398..b8a2ca7d9f 100644
--- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -3,27 +3,28 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.lfs.test
Bundle-SymbolicName: org.eclipse.jgit.lfs.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Import-Package: org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.attributes;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+Import-Package: org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.attributes;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.hamcrest.core;version="[1.1.0,3.0.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.lfs.test;version="7.0.0";x-friends:="org.eclipse.jgit.lfs.server.test"
+Export-Package: org.eclipse.jgit.lfs.test;version="7.3.0";x-friends:="org.eclipse.jgit.lfs.server.test"
diff --git a/org.eclipse.jgit.lfs.test/pom.xml b/org.eclipse.jgit.lfs.test/pom.xml
index 2c109c90ee..c9591b6078 100644
--- a/org.eclipse.jgit.lfs.test/pom.xml
+++ b/org.eclipse.jgit.lfs.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs.test</artifactId>
diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/internal/LfsConnectionFactoryTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/internal/LfsConnectionFactoryTest.java
index badcb7d7e5..ee8e893b3e 100644
--- a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/internal/LfsConnectionFactoryTest.java
+++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/internal/LfsConnectionFactoryTest.java
@@ -97,7 +97,8 @@ public class LfsConnectionFactoryTest extends RepositoryTestCase {
public void lfsUrlFromLocalConfig() throws Exception {
addRemoteUrl("https://localhost/repo");
- StoredConfig cfg = ((Repository) db).getConfig();
+ @SuppressWarnings("restriction")
+ StoredConfig cfg = db.getConfig();
cfg.setString(ConfigConstants.CONFIG_SECTION_LFS,
null,
ConfigConstants.CONFIG_KEY_URL,
@@ -111,7 +112,8 @@ public class LfsConnectionFactoryTest extends RepositoryTestCase {
public void lfsUrlFromOriginConfig() throws Exception {
addRemoteUrl("https://localhost/repo");
- StoredConfig cfg = ((Repository) db).getConfig();
+ @SuppressWarnings("restriction")
+ StoredConfig cfg = db.getConfig();
cfg.setString(ConfigConstants.CONFIG_SECTION_LFS,
org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME,
ConfigConstants.CONFIG_KEY_URL,
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
index f4c1e566a4..5338f8bb6d 100644
--- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -3,32 +3,32 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.lfs
Bundle-SymbolicName: org.eclipse.jgit.lfs
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.lfs;version="7.0.0",
- org.eclipse.jgit.lfs.errors;version="7.0.0",
- org.eclipse.jgit.lfs.internal;version="7.0.0";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
- org.eclipse.jgit.lfs.lib;version="7.0.0"
+Export-Package: org.eclipse.jgit.lfs;version="7.3.0",
+ org.eclipse.jgit.lfs.errors;version="7.3.0",
+ org.eclipse.jgit.lfs.internal;version="7.3.0";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
+ org.eclipse.jgit.lfs.lib;version="7.3.0"
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: com.google.gson;version="[2.8.2,3.0.0)",
com.google.gson.stream;version="[2.8.2,3.0.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)";resolution:=optional,
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.attributes;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.diff;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.dircache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.hooks;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.pack;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.io;version="[7.0.0,7.1.0)"
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)";resolution:=optional,
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.attributes;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.diff;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.dircache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.hooks;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.pack;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.io;version="[7.3.0,7.4.0)"
diff --git a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
index a048aa1738..fc124821ec 100644
--- a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.lfs - Sources
Bundle-SymbolicName: org.eclipse.jgit.lfs.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index 62508e245f..d336f16792 100644
--- a/org.eclipse.jgit.lfs/pom.xml
+++ b/org.eclipse.jgit.lfs/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs</artifactId>
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
index 75d500ef20..a13a60c2b8 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
@@ -41,24 +41,6 @@ public abstract class AnyLongObjectId implements Comparable<AnyLongObjectId> {
* @param secondObjectId
* the second identifier to compare. Must not be null.
* @return true if the two identifiers are the same.
- * @deprecated use {@link #isEqual(AnyLongObjectId, AnyLongObjectId)}
- * instead.
- */
- @Deprecated
- @SuppressWarnings("AmbiguousMethodReference")
- public static boolean equals(final AnyLongObjectId firstObjectId,
- final AnyLongObjectId secondObjectId) {
- return isEqual(firstObjectId, secondObjectId);
- }
-
- /**
- * Compare two object identifier byte sequences for equality.
- *
- * @param firstObjectId
- * the first identifier to compare. Must not be null.
- * @param secondObjectId
- * the second identifier to compare. Must not be null.
- * @return true if the two identifiers are the same.
* @since 5.4
*/
public static boolean isEqual(final AnyLongObjectId firstObjectId,
@@ -263,7 +245,7 @@ public abstract class AnyLongObjectId implements Comparable<AnyLongObjectId> {
*/
@SuppressWarnings({ "NonOverridingEquals", "AmbiguousMethodReference" })
public final boolean equals(AnyLongObjectId other) {
- return other != null ? equals(this, other) : false;
+ return other != null ? isEqual(this, other) : false;
}
@Override
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 3dd7b10079..adea43b8b5 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
index ea66a2383c..734b6347c7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
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 23fdd235cb..f86652fcba 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
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.gpg.bc"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
</url>
<requires>
- <import plugin="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import plugin="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml
index fc1bf49989..a756a8d12b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
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 92262a00be..d999c15c5f 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
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.http.apache"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
</url>
<requires>
- <import plugin="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import plugin="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
index 3344dcbb1b..c2ca95ae89 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
index 6f03419cb7..922622cda1 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.junit"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -24,7 +24,7 @@
<requires>
<import plugin="com.jcraft.jsch"/>
- <import plugin="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import plugin="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
index 45162c97a7..71e7373aaf 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
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 8eacae1fe7..53cb4bac60 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
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.lfs"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
</url>
<requires>
- <import feature="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
index 287a723b9c..31ba89faa0 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
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 179eab8a6b..2d7ee4e436 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
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.pgm"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -35,9 +35,9 @@
version="0.0.0"/>
<requires>
- <import feature="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
- <import feature="org.eclipse.jgit.lfs" version="7.0.0" match="equivalent"/>
- <import feature="org.eclipse.jgit.ssh.apache" version="7.0.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit.lfs" version="7.3.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit.ssh.apache" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
index cbb8e4fafc..2c80319b4d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
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 f46e08dfdc..eef699cfa1 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
@@ -123,12 +123,6 @@
<bundle id="org.eclipse.jetty.util.ajax.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>
@@ -147,10 +141,13 @@
<bundle id="org.apache.commons.commons-compress.source">
<category name="JGit-dependency-bundles"/>
</bundle>
- <bundle id="org.apache.commons.logging">
+ <bundle id="org.apache.commons.lang3">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.commons.lang3.source">
<category name="JGit-dependency-bundles"/>
</bundle>
- <bundle id="org.apache.commons.logging.source">
+ <bundle id="org.apache.commons.logging">
<category name="JGit-dependency-bundles"/>
</bundle>
<bundle id="org.apache.httpcomponents.httpclient">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
index 61b5dd6031..032072fc2a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.repository</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
index 4c0068bfab..139569dd01 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.source"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
</url>
<requires>
- <import feature="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
index 12ab06937a..43568f851e 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
@@ -30,7 +30,7 @@
<dependency>
<groupId>org.eclipse.jgit.feature</groupId>
<artifactId>org.eclipse.jgit</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
</dependencies>
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 209274feeb..7f80bbf6dd 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
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.ssh.apache"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
</url>
<requires>
- <import feature="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
index 1d82d4a1cc..fb9f73bdd9 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
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 ff46d9e65d..5eaa3b7542 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
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.ssh.jsch"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
</url>
<requires>
- <import plugin="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import plugin="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml
index c4d4fcf825..d5da669565 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
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
deleted file mode 100644
index 8899b518f0..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?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="1715125111">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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-09/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
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
deleted file mode 100644
index b3ff2050b7..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.17" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.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.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
deleted file mode 100644
index ee56a72584..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?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="1715125111">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </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
deleted file mode 100644
index 719476a829..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.18" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.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.19.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
deleted file mode 100644
index ad35e58e3f..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.19-staging" sequenceNumber="1715125111">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2021-03/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd
deleted file mode 100644
index 9eb4436772..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.19-staging" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2021-03/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
deleted file mode 100644
index 4031c045dc..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.20" sequenceNumber="1715125111">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2021-06/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd
deleted file mode 100644
index 264c040313..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.20" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2021-06/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
deleted file mode 100644
index 1a119f2f7e..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.21" sequenceNumber="1715125111">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2021-09/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd
deleted file mode 100644
index 5c7a112075..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.21" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2021-09/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target
deleted file mode 100644
index 1f6ee4eaac..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.22" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2021-12/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd
deleted file mode 100644
index ecc776e34c..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.22" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2021-12/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target
deleted file mode 100644
index d0a04209b3..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.23" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2022-03/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd
deleted file mode 100644
index 16efb408fb..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.23" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2022-03/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target
deleted file mode 100644
index dc9e090a23..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.24" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2022-06/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd
deleted file mode 100644
index d0f8e8d0b5..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.24" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2022-06/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.target
deleted file mode 100644
index 53dcb36667..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.25" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2022-09/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.tpd
deleted file mode 100644
index be37c10223..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.25" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2022-09/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.target
deleted file mode 100644
index 822c7cf41e..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.26" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2022-12/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.tpd
deleted file mode 100644
index e269919d49..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.26" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2022-12/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.target
deleted file mode 100644
index 9a0cab4248..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.27" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2023-03/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.tpd
deleted file mode 100644
index b67718a7c7..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.27" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2023-03/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.target
deleted file mode 100644
index 22aa30e607..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.28" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2023-06"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.tpd
deleted file mode 100644
index 1a9a22a5aa..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.28" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2023-06" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.target
deleted file mode 100644
index f7fb7c9e58..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.29" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2023-09"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.tpd
deleted file mode 100644
index 4e34280ec2..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.29" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2023-09" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.target
deleted file mode 100644
index 733559d660..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.30" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2023-12/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.tpd
deleted file mode 100644
index dfb44741e3..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.30" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/staging/2023-12/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.target
deleted file mode 100644
index 78ef16818c..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.31" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </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/2024-03/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.tpd
deleted file mode 100644
index 58491c85d5..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.31" with source configurePhase
-
-include "orbit/orbit-4.31.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/staging/2024-03/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.target
new file mode 100644
index 0000000000..60baf0b7cf
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.target
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.32" sequenceNumber="1740521280">
+ <locations>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
+ <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
+ <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
+ <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.junit" version="4.13.2.v20230809-1000"/>
+ <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
+ <unit id="org.objenesis" version="3.4.0"/>
+ <unit id="org.objenesis.source" version="3.4.0"/>
+ <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
+ <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
+ <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-06"/>
+ </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/2024-06/"/>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
+ <dependencies>
+ <dependency>
+ <groupId>org.tukaani</groupId>
+ <artifactId>xz</artifactId>
+ <version>1.10</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-osgi</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
+ <dependencies>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>5.15.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
+ <dependencies>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna-platform</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-servlet</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-security</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-session</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util-ajax</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
+ <version>6.1.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
+ <dependencies>
+ <dependency>
+ <groupId>com.googlecode.javaewah</groupId>
+ <artifactId>JavaEWAH</artifactId>
+ <version>1.2.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
+ <dependencies>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <version>2.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
+ <dependencies>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.12.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
+ <dependencies>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy-agent</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
+ <dependencies>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpg-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcutil-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
+ <dependencies>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.27.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
+ <dependencies>
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ <version>2.37</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
+ <dependencies>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>1.27.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.17.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.3.5</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ </locations>
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.tpd
new file mode 100644
index 0000000000..b8574c73b9
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.32" with source configurePhase
+
+include "orbit/orbit-4.32.tpd"
+include "maven/dependencies.tpd"
+
+location "https://download.eclipse.org/staging/2024-06/" {
+ org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target
new file mode 100644
index 0000000000..1558ad68fc
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.33" sequenceNumber="1740521283">
+ <locations>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
+ <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
+ <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
+ <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.junit" version="4.13.2.v20230809-1000"/>
+ <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
+ <unit id="org.objenesis" version="3.4.0"/>
+ <unit id="org.objenesis.source" version="3.4.0"/>
+ <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
+ <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
+ <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-09"/>
+ </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/2024-09/"/>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
+ <dependencies>
+ <dependency>
+ <groupId>org.tukaani</groupId>
+ <artifactId>xz</artifactId>
+ <version>1.10</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-osgi</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
+ <dependencies>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>5.15.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
+ <dependencies>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna-platform</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-servlet</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-security</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-session</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util-ajax</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
+ <version>6.1.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
+ <dependencies>
+ <dependency>
+ <groupId>com.googlecode.javaewah</groupId>
+ <artifactId>JavaEWAH</artifactId>
+ <version>1.2.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
+ <dependencies>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <version>2.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
+ <dependencies>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.12.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
+ <dependencies>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy-agent</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
+ <dependencies>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpg-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcutil-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
+ <dependencies>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.27.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
+ <dependencies>
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ <version>2.37</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
+ <dependencies>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>1.27.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.17.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.3.5</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ </locations>
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd
new file mode 100644
index 0000000000..74c687834b
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.33" with source configurePhase
+
+include "orbit/orbit-4.33.tpd"
+include "maven/dependencies.tpd"
+
+location "https://download.eclipse.org/releases/2024-09/" {
+ org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.target
new file mode 100644
index 0000000000..bc35d2c7eb
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.target
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.34" sequenceNumber="1740521284">
+ <locations>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
+ <unit id="org.apache.ant" version="1.10.15.v20240901-1000"/>
+ <unit id="org.apache.ant.source" version="1.10.15.v20240901-1000"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
+ <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.junit" version="4.13.2.v20240929-1000"/>
+ <unit id="org.junit.source" version="4.13.2.v20240929-1000"/>
+ <unit id="org.objenesis" version="3.4.0"/>
+ <unit id="org.objenesis.source" version="3.4.0"/>
+ <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
+ <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
+ <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-12"/>
+ </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/2024-12/"/>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
+ <dependencies>
+ <dependency>
+ <groupId>org.tukaani</groupId>
+ <artifactId>xz</artifactId>
+ <version>1.10</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-osgi</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
+ <dependencies>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>5.15.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
+ <dependencies>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna-platform</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-servlet</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-security</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-session</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util-ajax</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
+ <version>6.1.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
+ <dependencies>
+ <dependency>
+ <groupId>com.googlecode.javaewah</groupId>
+ <artifactId>JavaEWAH</artifactId>
+ <version>1.2.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
+ <dependencies>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <version>2.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
+ <dependencies>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.12.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
+ <dependencies>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy-agent</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
+ <dependencies>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpg-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcutil-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
+ <dependencies>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.27.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
+ <dependencies>
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ <version>2.37</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
+ <dependencies>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>1.27.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.17.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.3.5</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ </locations>
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.tpd
new file mode 100644
index 0000000000..4c38371748
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.34" with source configurePhase
+
+include "orbit/orbit-4.34.tpd"
+include "maven/dependencies.tpd"
+
+location "https://download.eclipse.org/staging/2024-12/" {
+ org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.target
new file mode 100644
index 0000000000..15cabc3d1f
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.target
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.35" sequenceNumber="1740521286">
+ <locations>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
+ <unit id="org.apache.ant" version="1.10.15.v20240901-1000"/>
+ <unit id="org.apache.ant.source" version="1.10.15.v20240901-1000"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
+ <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.junit" version="4.13.2.v20240929-1000"/>
+ <unit id="org.junit.source" version="4.13.2.v20240929-1000"/>
+ <unit id="org.objenesis" version="3.4.0"/>
+ <unit id="org.objenesis.source" version="3.4.0"/>
+ <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
+ <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
+ <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2025-03"/>
+ </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/2025-03/"/>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
+ <dependencies>
+ <dependency>
+ <groupId>org.tukaani</groupId>
+ <artifactId>xz</artifactId>
+ <version>1.10</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-osgi</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
+ <dependencies>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>5.15.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
+ <dependencies>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna-platform</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-servlet</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-security</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-session</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util-ajax</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
+ <version>6.1.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
+ <dependencies>
+ <dependency>
+ <groupId>com.googlecode.javaewah</groupId>
+ <artifactId>JavaEWAH</artifactId>
+ <version>1.2.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
+ <dependencies>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <version>2.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
+ <dependencies>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.12.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
+ <dependencies>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy-agent</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
+ <dependencies>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpg-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcutil-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
+ <dependencies>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.27.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
+ <dependencies>
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ <version>2.37</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
+ <dependencies>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>1.27.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.17.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.3.5</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ </locations>
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.tpd
new file mode 100644
index 0000000000..3c0646eb46
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.35" with source configurePhase
+
+include "orbit/orbit-4.35.tpd"
+include "maven/dependencies.tpd"
+
+location "https://download.eclipse.org/staging/2025-03/" {
+ org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd
index a25f9c9650..b292cf5e84 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd
@@ -10,22 +10,27 @@ maven apache
dependency {
groupId = "commons-codec"
artifactId = "commons-codec"
- version = "1.16.0"
+ version = "1.18.0"
}
dependency {
groupId = "org.apache.commons"
artifactId = "commons-compress"
- version = "1.26.0"
+ version = "1.27.1"
+ }
+ dependency {
+ groupId = "org.apache.commons"
+ artifactId = "commons-lang3"
+ version = "3.17.0"
}
dependency {
groupId = "commons-io"
artifactId = "commons-io"
- version = "2.15.1"
+ version = "2.18.0"
}
dependency {
groupId = "commons-logging"
artifactId = "commons-logging"
- version = "1.2"
+ version = "1.3.5"
}
}
@@ -38,7 +43,7 @@ maven args4j
dependency {
groupId = "args4j"
artifactId = "args4j"
- version = "2.33"
+ version = "2.37"
}
}
@@ -51,7 +56,7 @@ maven assertj
dependency {
groupId = "org.assertj"
artifactId = "assertj-core"
- version = "3.25.3"
+ version = "3.27.3"
}
}
@@ -64,22 +69,22 @@ maven bouncycastle
dependency {
groupId = "org.bouncycastle"
artifactId = "bcpg-jdk18on"
- version = "1.77"
+ version = "1.80"
}
dependency {
groupId = "org.bouncycastle"
artifactId = "bcprov-jdk18on"
- version = "1.77"
+ version = "1.80"
}
dependency {
groupId = "org.bouncycastle"
artifactId = "bcpkix-jdk18on"
- version = "1.77"
+ version = "1.80"
}
dependency {
groupId = "org.bouncycastle"
artifactId = "bcutil-jdk18on"
- version = "1.77"
+ version = "1.80"
}
}
@@ -92,12 +97,12 @@ maven bytebuddy
dependency {
groupId = "net.bytebuddy"
artifactId = "byte-buddy"
- version = "1.14.12"
+ version = "1.17.1"
}
dependency {
groupId = "net.bytebuddy"
artifactId = "byte-buddy-agent"
- version = "1.14.12"
+ version = "1.17.1"
}
}
@@ -110,7 +115,7 @@ maven gson
dependency {
groupId = "com.google.code.gson"
artifactId = "gson"
- version = "2.10.1"
+ version = "2.12.1"
}
}
@@ -149,47 +154,47 @@ maven jetty
dependency {
groupId = "org.eclipse.jetty.ee10"
artifactId = "jetty-ee10-servlet"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-http"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-io"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-security"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-server"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-session"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-util"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-util-ajax"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "jakarta.servlet"
artifactId = "jakarta.servlet-api"
- version = "6.0.0"
+ version = "6.1.0"
}
}
@@ -202,12 +207,12 @@ maven jna
dependency {
groupId = "net.java.dev.jna"
artifactId = "jna"
- version = "5.14.0"
+ version = "5.16.0"
}
dependency {
groupId = "net.java.dev.jna"
artifactId = "jna-platform"
- version = "5.14.0"
+ version = "5.16.0"
}
}
@@ -220,7 +225,7 @@ maven mockito
dependency {
groupId = "org.mockito"
artifactId = "mockito-core"
- version = "5.10.0"
+ version = "5.15.2"
}
}
@@ -233,12 +238,12 @@ maven sshd
dependency {
groupId = "org.apache.sshd"
artifactId = "sshd-osgi"
- version = "2.12.0"
+ version = "2.15.0"
}
dependency {
groupId = "org.apache.sshd"
artifactId = "sshd-sftp"
- version = "2.12.0"
+ version = "2.15.0"
}
}
@@ -269,6 +274,6 @@ maven xz
dependency {
groupId = "org.tukaani"
artifactId = "xz"
- version = "1.9"
+ version = "1.10"
}
} \ No newline at end of file
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20200831200620-2020-09.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20200831200620-2020-09.tpd
deleted file mode 100644
index 22e2b01207..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20200831200620-2020-09.tpd
+++ /dev/null
@@ -1,66 +0,0 @@
-target "R20200831200620-2020-09" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20200831200620/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]
- 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.8.v20200515-1239,1.10.8.v20200515-1239]
- org.apache.ant.source [1.10.8.v20200515-1239,1.10.8.v20200515-1239]
- 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.10.v20200830-2311,4.5.10.v20200830-2311]
- org.apache.httpcomponents.httpclient.source [4.5.10.v20200830-2311,4.5.10.v20200830-2311]
- org.apache.httpcomponents.httpcore [4.4.12.v20200108-1212,4.4.12.v20200108-1212]
- org.apache.httpcomponents.httpcore.source [4.4.12.v20200108-1212,4.4.12.v20200108-1212]
- 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.4.0.v20200318-1614,2.4.0.v20200318-1614]
- org.apache.sshd.osgi.source [2.4.0.v20200318-1614,2.4.0.v20200318-1614]
- org.apache.sshd.sftp [2.4.0.v20200319-1547,2.4.0.v20200319-1547]
- org.apache.sshd.sftp.source [2.4.0.v20200319-1547,2.4.0.v20200319-1547]
- 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.2.v20121108-1250,1.7.2.v20121108-1250]
- org.slf4j.api.source [1.7.2.v20121108-1250,1.7.2.v20121108-1250]
- org.slf4j.impl.log4j12 [1.7.2.v20131105-2200,1.7.2.v20131105-2200]
- org.slf4j.impl.log4j12.source [1.7.2.v20131105-2200,1.7.2.v20131105-2200]
- 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/orbit/R20201130205003-2020-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20201130205003-2020-12.tpd
deleted file mode 100644
index 08a0846de7..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20201130205003-2020-12.tpd
+++ /dev/null
@@ -1,66 +0,0 @@
-target "R20201130205003-2020-12" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-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]
- 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.10.v20200830-2311,4.5.10.v20200830-2311]
- org.apache.httpcomponents.httpclient.source [4.5.10.v20200830-2311,4.5.10.v20200830-2311]
- org.apache.httpcomponents.httpcore [4.4.12.v20200108-1212,4.4.12.v20200108-1212]
- org.apache.httpcomponents.httpcore.source [4.4.12.v20200108-1212,4.4.12.v20200108-1212]
- 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.4.0.v20200318-1614,2.4.0.v20200318-1614]
- org.apache.sshd.osgi.source [2.4.0.v20200318-1614,2.4.0.v20200318-1614]
- org.apache.sshd.sftp [2.4.0.v20200319-1547,2.4.0.v20200319-1547]
- org.apache.sshd.sftp.source [2.4.0.v20200319-1547,2.4.0.v20200319-1547]
- 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/orbit/R20210223232630-2021-03.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210223232630-2021-03.tpd
deleted file mode 100644
index 605a43bb13..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210223232630-2021-03.tpd
+++ /dev/null
@@ -1,66 +0,0 @@
-target "R20210223232630-2021-03" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20210223232630/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/orbit/R20210602031627-2021-06.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210602031627-2021-06.tpd
deleted file mode 100644
index 83b5bb3fd2..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210602031627-2021-06.tpd
+++ /dev/null
@@ -1,66 +0,0 @@
-target "R20210602031627-2021-06" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20210602031627/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.10.v20210426-1926,1.10.10.v20210426-1926]
- org.apache.ant.source [1.10.10.v20210426-1926,1.10.10.v20210426-1926]
- 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/orbit/R20210825222808-2021-09.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210825222808-2021-09.tpd
deleted file mode 100644
index 99f352011b..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210825222808-2021-09.tpd
+++ /dev/null
@@ -1,73 +0,0 @@
-target "R20210825222808-2021-09" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20210825222808/repository" {
- com.google.gson [2.8.7.v20210624-1215,2.8.7.v20210624-1215]
- com.google.gson.source [2.8.7.v20210624-1215,2.8.7.v20210624-1215]
- 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]
- com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- javaewah [1.1.12.v20210622-2206,1.1.12.v20210622-2206]
- javaewah.source [1.1.12.v20210622-2206,1.1.12.v20210622-2206]
- 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.11.v20210720-1445,1.10.11.v20210720-1445]
- org.apache.ant.source [1.10.11.v20210720-1445,1.10.11.v20210720-1445]
- 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.20.0.v20210713-1928,1.20.0.v20210713-1928]
- org.apache.commons.compress.source [1.20.0.v20210713-1928,1.20.0.v20210713-1928]
- 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.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.osgi.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.sftp [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.sftp.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpg.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpkix [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpkix.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcprov [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcprov.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcutil [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcutil.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- 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.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211122181901-2021-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211122181901-2021-12.tpd
deleted file mode 100644
index cd1d1c08fa..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211122181901-2021-12.tpd
+++ /dev/null
@@ -1,71 +0,0 @@
-target "R20211122181901-2021-12" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20211122181901/repository" {
- com.google.gson [2.8.8.v20211029-0838,2.8.8.v20211029-0838]
- com.google.gson.source [2.8.8.v20211029-0838,2.8.8.v20211029-0838]
- 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]
- com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- 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.v20210923-1401,0.3.0.v20210923-1401]
- net.i2p.crypto.eddsa.source [0.3.0.v20210923-1401,0.3.0.v20210923-1401]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- 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.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- 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.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.osgi.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.sftp [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.sftp.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpg.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpkix [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpkix.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcprov [1.69.0.v20210923-1401,1.69.0.v20210923-1401]
- org.bouncycastle.bcprov.source [1.69.0.v20210923-1401,1.69.0.v20210923-1401]
- org.bouncycastle.bcutil [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcutil.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- 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.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- 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.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211213173813-2021-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211213173813-2021-12.tpd
deleted file mode 100644
index 0c7c846738..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211213173813-2021-12.tpd
+++ /dev/null
@@ -1,69 +0,0 @@
-target "R20211213173813-2021-12" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20211213173813/repository" {
- com.google.gson [2.8.8.v20211029-0838,2.8.8.v20211029-0838]
- com.google.gson.source [2.8.8.v20211029-0838,2.8.8.v20211029-0838]
- 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]
- com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- 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.v20210923-1401,0.3.0.v20210923-1401]
- net.i2p.crypto.eddsa.source [0.3.0.v20210923-1401,0.3.0.v20210923-1401]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- 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.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- 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.sshd.osgi [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.osgi.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.sftp [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.sftp.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpg.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpkix [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpkix.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcprov [1.69.0.v20210923-1401,1.69.0.v20210923-1401]
- org.bouncycastle.bcprov.source [1.69.0.v20210923-1401,1.69.0.v20210923-1401]
- org.bouncycastle.bcutil [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcutil.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- 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.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- 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.simple [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.simple.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220302172233-2022-03.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220302172233-2022-03.tpd
deleted file mode 100644
index fafc689268..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220302172233-2022-03.tpd
+++ /dev/null
@@ -1,69 +0,0 @@
-target "R20220302172233" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20220302172233/repository" {
- com.google.gson [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
- com.google.gson.source [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
- 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]
- com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- 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.v20210923-1401,0.3.0.v20210923-1401]
- net.i2p.crypto.eddsa.source [0.3.0.v20210923-1401,0.3.0.v20210923-1401]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- 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.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- 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.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.httpcomponents.httpcore.source [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.sshd.osgi [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.osgi.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.sftp [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.sftp.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcpg.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcpkix [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcpkix.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcprov [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcprov.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcutil [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcutil.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- 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.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- 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.simple [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.simple.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220531185310-2022-06.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220531185310-2022-06.tpd
deleted file mode 100644
index 3c74497c21..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220531185310-2022-06.tpd
+++ /dev/null
@@ -1,69 +0,0 @@
-target "R20220531185310-2022-06" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository" {
- com.google.gson [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
- com.google.gson.source [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
- 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.3.v20220502-1820,1.1.3.v20220502-1820]
- com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- 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.v20220506-1020,0.3.0.v20220506-1020]
- net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- 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.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- 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.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.httpcomponents.httpcore.source [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.sshd.osgi [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.osgi.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.sftp [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.sftp.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.70.0.v20220507-1208,1.70.0.v20220507-1208]
- org.bouncycastle.bcpg.source [1.70.0.v20220507-1208,1.70.0.v20220507-1208]
- org.bouncycastle.bcpkix [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcpkix.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcprov [1.70.0.v20220507-1208,1.70.0.v20220507-1208]
- org.bouncycastle.bcprov.source [1.70.0.v20220507-1208,1.70.0.v20220507-1208]
- org.bouncycastle.bcutil [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcutil.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- 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.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- 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.simple [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.simple.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220830213456-2022-09.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220830213456-2022-09.tpd
deleted file mode 100644
index 8db1018ffb..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220830213456-2022-09.tpd
+++ /dev/null
@@ -1,69 +0,0 @@
-target "R20220830213456-2022-09" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository" {
- com.google.gson [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
- com.google.gson.source [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
- 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.3.v20220502-1820,1.1.3.v20220502-1820]
- com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- 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.v20220506-1020,0.3.0.v20220506-1020]
- net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- 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.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- 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.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.httpcomponents.httpcore.source [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.sshd.osgi [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.osgi.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.sftp [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.sftp.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcpg.source [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcpkix [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcpkix.source [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcprov [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcprov.source [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcutil [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcutil.source [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- 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.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- 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.simple [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.simple.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20221123021534-2022-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20221123021534-2022-12.tpd
deleted file mode 100644
index 378b84873f..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20221123021534-2022-12.tpd
+++ /dev/null
@@ -1,69 +0,0 @@
-target "S20230101190934" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20221123021534/repository" {
- com.google.gson [2.9.1.v20220915-1632,2.9.1.v20220915-1632]
- com.google.gson.source [2.9.1.v20220915-1632,2.9.1.v20220915-1632]
- com.jcraft.jsch [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jsch.source [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jzlib [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.sun.jna [5.12.1.v20221103-2317,5.12.1.v20221103-2317]
- com.sun.jna.source [5.12.1.v20221103-2317,5.12.1.v20221103-2317]
- com.sun.jna.platform [5.12.1.v20221103-2317,5.12.1.v20221103-2317]
- com.sun.jna.platform.source [5.12.1.v20221103-2317,5.12.1.v20221103-2317]
- javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- net.bytebuddy.byte-buddy [1.12.18.v20221114-2102,1.12.18.v20221114-2102]
- net.bytebuddy.byte-buddy.source [1.12.18.v20221114-2102,1.12.18.v20221114-2102]
- net.bytebuddy.byte-buddy-agent [1.12.18.v20221114-2102,1.12.18.v20221114-2102]
- net.bytebuddy.byte-buddy-agent.source [1.12.18.v20221114-2102,1.12.18.v20221114-2102]
- net.i2p.crypto.eddsa [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.commons.codec [1.14.0.v20221112-0806,1.14.0.v20221112-0806]
- org.apache.commons.codec.source [1.14.0.v20221112-0806,1.14.0.v20221112-0806]
- org.apache.commons.compress [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- 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.v20221112-0806,4.5.13.v20221112-0806]
- org.apache.httpcomponents.httpclient.source [4.5.13.v20221112-0806,4.5.13.v20221112-0806]
- org.apache.httpcomponents.httpcore [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.httpcomponents.httpcore.source [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.sshd.osgi [2.9.2.v20221117-1942,2.9.2.v20221117-1942]
- org.apache.sshd.osgi.source [2.9.2.v20221117-1942,2.9.2.v20221117-1942]
- org.apache.sshd.sftp [2.9.2.v20221117-1942,2.9.2.v20221117-1942]
- org.apache.sshd.sftp.source [2.9.2.v20221117-1942,2.9.2.v20221117-1942]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcpg.source [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcpkix [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcpkix.source [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcprov [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcprov.source [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcutil [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcutil.source [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- 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.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- 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.mockito-core [4.8.1.v20221103-2317,4.8.1.v20221103-2317]
- org.mockito.mockito-core.source [4.8.1.v20221103-2317,4.8.1.v20221103-2317]
- org.objenesis [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
- org.objenesis.source [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
- org.slf4j.api [1.7.30.v20221112-0806,1.7.30.v20221112-0806]
- org.slf4j.api.source [1.7.30.v20221112-0806,1.7.30.v20221112-0806]
- org.slf4j.binding.simple [1.7.30.v20221112-0806,1.7.30.v20221112-0806]
- org.slf4j.binding.simple.source [1.7.30.v20221112-0806,1.7.30.v20221112-0806]
- org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230302014618-2023-03.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230302014618-2023-03.tpd
deleted file mode 100644
index 8578b2cdf5..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230302014618-2023-03.tpd
+++ /dev/null
@@ -1,27 +0,0 @@
-target "R20230302014618-2023-03" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/repository" {
- com.jcraft.jsch [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jsch.source [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jzlib [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- net.i2p.crypto.eddsa [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.httpcomponents.httpclient [4.5.14.v20221207-1049,4.5.14.v20221207-1049]
- org.apache.httpcomponents.httpclient.source [4.5.14.v20221207-1049,4.5.14.v20221207-1049]
- org.apache.httpcomponents.httpcore [4.4.16.v20221207-1049,4.4.16.v20221207-1049]
- org.apache.httpcomponents.httpcore.source [4.4.16.v20221207-1049,4.4.16.v20221207-1049]
- 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.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.mockito.mockito-core [4.8.1.v20221103-2317,4.8.1.v20221103-2317]
- org.mockito.mockito-core.source [4.8.1.v20221103-2317,4.8.1.v20221103-2317]
- org.objenesis [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
- org.objenesis.source [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230531010532-2023-06.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230531010532-2023-06.tpd
deleted file mode 100644
index 46055d3728..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230531010532-2023-06.tpd
+++ /dev/null
@@ -1,25 +0,0 @@
-target "R20230531010532-2023-06" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20230531010532/repository" {
- com.jcraft.jsch [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jsch.source [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jzlib [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- net.i2p.crypto.eddsa [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.httpcomponents.httpclient [4.5.14.v20230516-1249,4.5.14.v20230516-1249]
- org.apache.httpcomponents.httpclient.source [4.5.14.v20230516-1249,4.5.14.v20230516-1249]
- org.apache.httpcomponents.httpcore [4.4.16.v20221207-1049,4.4.16.v20221207-1049]
- org.apache.httpcomponents.httpcore.source [4.4.16.v20221207-1049,4.4.16.v20221207-1049]
- 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.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.objenesis [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
- org.objenesis.source [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.29.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.29.tpd
deleted file mode 100644
index 70a17a1ddc..0000000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.29.tpd
+++ /dev/null
@@ -1,27 +0,0 @@
-target "orbit-4.29" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/release/4.29.0" {
- com.jcraft.jsch [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jsch.source [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jzlib [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- net.i2p.crypto.eddsa [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
- org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
- org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
- org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16]
- org.hamcrest.core [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.junit [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.junit.source [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.objenesis [3.3,3.3]
- org.objenesis.source [3.3,3.3]
- org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
- org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.31.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.32.tpd
index 0554a8578c..59fcd8745a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.31.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.32.tpd
@@ -1,13 +1,11 @@
-target "orbit-4.30" with source configurePhase
+target "orbit-4.32" with source configurePhase
// see https://download.eclipse.org/tools/orbit/downloads/
-location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12" {
+location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-06" {
com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
- net.i2p.crypto.eddsa [0.3.0,0.3.0]
- net.i2p.crypto.eddsa.source [0.3.0,0.3.0]
org.apache.ant [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
org.apache.ant.source [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
@@ -20,8 +18,8 @@ location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023
org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
org.junit [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
org.junit.source [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.objenesis [3.3,3.3]
- org.objenesis.source [3.3,3.3]
+ org.objenesis [3.4,3.4]
+ org.objenesis.source [3.4,3.4]
org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.33.tpd
index 0554a8578c..2cfa0a8e7c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.33.tpd
@@ -1,13 +1,11 @@
-target "orbit-4.30" with source configurePhase
+target "orbit-4.33" with source configurePhase
// see https://download.eclipse.org/tools/orbit/downloads/
-location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12" {
+location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-09" {
com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
- net.i2p.crypto.eddsa [0.3.0,0.3.0]
- net.i2p.crypto.eddsa.source [0.3.0,0.3.0]
org.apache.ant [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
org.apache.ant.source [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
@@ -20,8 +18,8 @@ location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023
org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
org.junit [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
org.junit.source [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.objenesis [3.3,3.3]
- org.objenesis.source [3.3,3.3]
+ org.objenesis [3.4,3.4]
+ org.objenesis.source [3.4,3.4]
org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.34.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.34.tpd
new file mode 100644
index 0000000000..d3e15bba6d
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.34.tpd
@@ -0,0 +1,25 @@
+target "orbit-4.34" with source configurePhase
+// see https://download.eclipse.org/tools/orbit/downloads/
+
+location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-12" {
+ com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
+ com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
+ com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
+ com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
+ org.apache.ant [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
+ org.apache.ant.source [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
+ org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
+ org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
+ org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
+ org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16]
+ org.hamcrest.core [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
+ org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
+ org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
+ org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
+ org.junit [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+ org.junit.source [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+ org.objenesis [3.4,3.4]
+ org.objenesis.source [3.4,3.4]
+ org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
+ org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.35.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.35.tpd
new file mode 100644
index 0000000000..ec6996e427
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.35.tpd
@@ -0,0 +1,25 @@
+target "orbit-4.35" with source configurePhase
+// see https://download.eclipse.org/tools/orbit/downloads/
+
+location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2025-03" {
+ com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
+ com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
+ com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
+ com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
+ org.apache.ant [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
+ org.apache.ant.source [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
+ org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
+ org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
+ org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
+ org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16]
+ org.hamcrest.core [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
+ org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
+ org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
+ org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
+ org.junit [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+ org.junit.source [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+ org.objenesis [3.4,3.4]
+ org.objenesis.source [3.4,3.4]
+ org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
+ org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
+}
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 06cd76cd0d..6352d0aae3 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -16,7 +16,7 @@
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>JGit Tycho Parent</name>
@@ -30,8 +30,8 @@
<properties>
<java.version>17</java.version>
- <tycho-version>4.0.6</tycho-version>
- <target-platform>jgit-4.17</target-platform>
+ <tycho-version>4.0.11</tycho-version>
+ <target-platform>jgit-4.32</target-platform>
<project.build.outputTimestamp>${git.commit.time}</project.build.outputTimestamp>
</properties>
@@ -174,7 +174,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
- <version>3.4.1</version>
+ <version>3.5.0</version>
<executions>
<execution>
<id>enforce-maven</id>
@@ -184,7 +184,7 @@
<configuration>
<rules>
<requireMavenVersion>
- <version>3.6.3</version>
+ <version>3.9.0</version>
</requireMavenVersion>
</rules>
</configuration>
@@ -204,7 +204,7 @@
<plugin>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
- <version>2.7.11</version>
+ <version>2.9.1</version>
<configuration>
<projectType>library</projectType>
<schemaVersion>1.4</schemaVersion>
@@ -233,7 +233,7 @@
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
- <version>7.0.0</version>
+ <version>9.0.1</version>
<executions>
<execution>
<id>get-the-git-infos</id>
@@ -273,7 +273,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>3.3.0</version>
+ <version>3.4.2</version>
<configuration>
<archive>
<manifestEntries>
@@ -381,36 +381,36 @@
<plugin>
<groupId>org.eclipse.cbi.maven.plugins</groupId>
<artifactId>eclipse-jarsigner-plugin</artifactId>
- <version>1.4.3</version>
+ <version>1.5.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
- <version>3.5.0</version>
+ <version>3.6.0</version>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
- <version>3.3.2</version>
+ <version>3.4.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
- <version>3.1.1</version>
+ <version>3.1.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
- <version>3.1.1</version>
+ <version>3.1.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
- <version>4.0.0-M13</version>
+ <version>4.0.0-M16</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-artifact-plugin</artifactId>
- <version>3.5.0</version>
+ <version>3.6.0</version>
<configuration>
<ignore>**/*cyclonedx.json</ignore>
<reproducible>true</reproducible>
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index 955c5c04b5..2056f0b0aa 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -3,30 +3,30 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.pgm.test
Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Import-Package: org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.diff;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.dircache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.merge;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.pgm;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.pgm.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.pgm.opt;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.io;version="[7.0.0,7.1.0)",
+Import-Package: org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.diff;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.dircache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.merge;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.pgm;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.pgm.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.pgm.opt;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.io;version="[7.3.0,7.4.0)",
org.hamcrest.core;bundle-version="[1.1.0,3.0.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.rules;version="[4.13,5.0.0)",
diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml
index 90090d6958..24c289c227 100644
--- a/org.eclipse.jgit.pgm.test/pom.xml
+++ b/org.eclipse.jgit.pgm.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.pgm.test</artifactId>
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java
index 6d6374f172..a48fcbcd5a 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Google Inc. and others
+ * Copyright (C) 2012, 2025 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,7 +12,10 @@ package org.eclipse.jgit.pgm;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.dircache.DirCache;
@@ -32,12 +35,7 @@ public class AddTest extends CLIRepositoryTestCase {
@Test
public void testAddNothing() throws Exception {
- try {
- execute("git add");
- fail("Must die");
- } catch (Die e) {
- // expected, requires argument
- }
+ assertThrows(Die.class, () -> execute("git add"));
}
@Test
@@ -46,6 +44,17 @@ public class AddTest extends CLIRepositoryTestCase {
}
@Test
+ public void testAddInvalidOptionCombinations() throws Exception {
+ writeTrashFile("greeting", "Hello, world!");
+ assertThrows(Die.class, () -> execute("git add -u -A greeting"));
+ assertThrows(Die.class,
+ () -> execute("git add -u --ignore-removed greeting"));
+ // --renormalize implies -u
+ assertThrows(Die.class,
+ () -> execute("git add --renormalize --all greeting"));
+ }
+
+ @Test
public void testAddAFile() throws Exception {
writeTrashFile("greeting", "Hello, world!");
assertArrayEquals(new String[] { "" }, //
@@ -78,4 +87,34 @@ public class AddTest extends CLIRepositoryTestCase {
assertNotNull(cache.getEntry("greeting"));
assertEquals(1, cache.getEntryCount());
}
+
+ @Test
+ public void testAddDeleted() throws Exception {
+ File greeting = writeTrashFile("greeting", "Hello, world!");
+ git.add().addFilepattern("greeting").call();
+ DirCache cache = db.readDirCache();
+ assertNotNull(cache.getEntry("greeting"));
+ assertEquals(1, cache.getEntryCount());
+ assertTrue(greeting.delete());
+ assertArrayEquals(new String[] { "" }, //
+ execute("git add greeting"));
+
+ cache = db.readDirCache();
+ assertEquals(0, cache.getEntryCount());
+ }
+
+ @Test
+ public void testAddDeleted2() throws Exception {
+ File greeting = writeTrashFile("greeting", "Hello, world!");
+ git.add().addFilepattern("greeting").call();
+ DirCache cache = db.readDirCache();
+ assertNotNull(cache.getEntry("greeting"));
+ assertEquals(1, cache.getEntryCount());
+ assertTrue(greeting.delete());
+ assertArrayEquals(new String[] { "" }, //
+ execute("git add -A"));
+
+ cache = db.readDirCache();
+ assertEquals(0, cache.getEntryCount());
+ }
}
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 a1fb9fb589..c56cc6bf3c 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
@@ -126,7 +126,7 @@ public class CloneTest extends CLIRepositoryTestCase {
JGitTestUtil.writeTrashFile(db, "Test.txt", "Some change");
git.add().addFilepattern("Test.txt").call();
return git.commit()
- .setCommitter(new PersonIdent(this.committer, tr.getDate()))
+ .setCommitter(new PersonIdent(this.committer, tr.getInstant()))
.setMessage("Second commit").call();
}
@@ -134,7 +134,7 @@ public class CloneTest extends CLIRepositoryTestCase {
JGitTestUtil.writeTrashFile(db, "change.txt", "another change");
git.add().addFilepattern("change.txt").call();
return git.commit()
- .setCommitter(new PersonIdent(this.committer, tr.getDate()))
+ .setCommitter(new PersonIdent(this.committer, tr.getInstant()))
.setMessage("Third commit").call();
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java
index c78544309b..595767d3a0 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java
@@ -123,6 +123,15 @@ public class DescribeTest extends CLIRepositoryTestCase {
}
@Test
+ public void testDescribeExclude() throws Exception {
+ initialCommitAndTag();
+ secondCommit();
+ git.tag().setName("v2.0").call();
+ assertArrayEquals(new String[] { "v1.0-1-g56f6ceb", "" },
+ execute("git describe --exclude v2.*"));
+ }
+
+ @Test
public void testDescribeCommitMultiMatch() throws Exception {
initialCommitAndTag();
secondCommit();
@@ -133,6 +142,17 @@ public class DescribeTest extends CLIRepositoryTestCase {
}
@Test
+ public void testDescribeCommitMultiExclude() throws Exception {
+ initialCommitAndTag();
+ secondCommit();
+ git.tag().setName("v2.0.0").call();
+ git.tag().setName("v2.1.1").call();
+ git.tag().setName("v2.2").call();
+ assertArrayEquals("git yields v2.2", new String[] { "v2.2", "" },
+ execute("git describe --exclude v2.0* --exclude v2.1.*"));
+ }
+
+ @Test
public void testDescribeCommitNoMatch() throws Exception {
initialCommitAndTag();
writeTrashFile("greeting", "Hello, world!");
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java
index 54c4f26099..6339831a40 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java
@@ -27,6 +27,7 @@ import org.eclipse.jgit.internal.diffmergetool.ExternalMergeTool;
import org.eclipse.jgit.internal.diffmergetool.MergeTools;
import org.eclipse.jgit.lib.StoredConfig;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
/**
@@ -77,6 +78,7 @@ public class MergeToolTest extends ToolTestCase {
+ errorReturnCode);
}
+ @Ignore
@Test
public void testEmptyToolName() throws Exception {
assumeLinuxPlatform();
@@ -91,7 +93,7 @@ public class MergeToolTest extends ToolTestCase {
createMergeConflict();
- String araxisErrorLine = "compare: unrecognized option `-wait' @ error/compare.c/CompareImageCommand/1123.";
+ String araxisErrorLine = "compare-im6.q16: unrecognized option `-wait' @ error/compare.c/CompareImageCommand/1131.";
String[] expectedErrorOutput = { araxisErrorLine, araxisErrorLine, };
runAndCaptureUsingInitRaw(Arrays.asList(expectedErrorOutput),
MERGE_TOOL, "--no-prompt");
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/PackRefsTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/PackRefsTest.java
new file mode 100644
index 0000000000..b4d4ea9e56
--- /dev/null
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/PackRefsTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.pgm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PackRefsTest extends CLIRepositoryTestCase {
+ private Git git;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ git = new Git(db);
+ git.commit().setMessage("initial commit").call();
+ }
+
+ @Test
+ public void tagPacked() throws Exception {
+ git.tag().setName("test").call();
+ git.packRefs().call();
+ assertEquals(Ref.Storage.PACKED,
+ git.getRepository().exactRef("refs/tags/test").getStorage());
+ }
+
+ @Test
+ public void nonTagRefNotPackedWithoutAll() throws Exception {
+ git.branchCreate().setName("test").call();
+ git.packRefs().call();
+ assertEquals(Ref.Storage.LOOSE,
+ git.getRepository().exactRef("refs/heads/test").getStorage());
+ }
+
+ @Test
+ public void nonTagRefPackedWithAll() throws Exception {
+ git.branchCreate().setName("test").call();
+ git.packRefs().setAll(true).call();
+ assertEquals(Ref.Storage.PACKED,
+ git.getRepository().exactRef("refs/heads/test").getStorage());
+ }
+
+ @Test
+ public void refTableCompacted() throws Exception {
+ ((FileRepository) git.getRepository()).convertRefStorage(
+ ConfigConstants.CONFIG_REF_STORAGE_REFTABLE, false, false);
+
+ git.commit().setMessage("test commit").call();
+ File tableDir = new File(db.getDirectory(), Constants.REFTABLE);
+ File[] reftables = tableDir.listFiles();
+ assertNotNull(reftables);
+ assertTrue(reftables.length > 2);
+
+ git.packRefs().call();
+
+ reftables = tableDir.listFiles();
+ assertNotNull(reftables);
+ assertEquals(2, reftables.length);
+ }
+}
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index bb0c23dd72..d91efd46dd 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.pgm
Bundle-SymbolicName: org.eclipse.jgit.pgm
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -14,49 +14,50 @@ Import-Package: jakarta.servlet;version="[6.0.0,7.0.0)",
org.eclipse.jetty.server.handler;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.component;version="[12.0.0,13.0.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.archive;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.awtui;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.blame;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.diff;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.dircache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.gitrepo;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.io;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.pack;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.server;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.server.fs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.server.s3;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.merge;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.notes;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revplot;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.pack;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http.apache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.resolver;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.ssh.jsch;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.sshd;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.io;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.archive;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.awtui;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.blame;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.diff;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.dircache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.gitrepo;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.io;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.midx;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.server;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.merge;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.notes;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revplot;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.pack;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.ssh.jsch;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.sshd;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.io;version="[7.3.0,7.4.0)",
org.kohsuke.args4j;version="[2.33.0,3.0.0)",
org.kohsuke.args4j.spi;version="[2.33.0,3.0.0)"
-Export-Package: org.eclipse.jgit.console;version="7.0.0";
+Export-Package: org.eclipse.jgit.console;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="7.0.0";
+ org.eclipse.jgit.pgm;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.util.io,
org.eclipse.jgit.awtui,
@@ -68,14 +69,14 @@ Export-Package: org.eclipse.jgit.console;version="7.0.0";
org.eclipse.jgit.treewalk,
org.eclipse.jgit.api,
javax.swing",
- org.eclipse.jgit.pgm.debug;version="7.0.0";
+ org.eclipse.jgit.pgm.debug;version="7.3.0";
uses:="org.eclipse.jgit.util.io,
org.eclipse.jgit.pgm,
org.eclipse.jetty.servlet",
- org.eclipse.jgit.pgm.internal;version="7.0.0";
+ org.eclipse.jgit.pgm.internal;version="7.3.0";
x-friends:="org.eclipse.jgit.pgm.test,
org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="7.0.0";
+ org.eclipse.jgit.pgm.opt;version="7.3.0";
uses:="org.kohsuke.args4j,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
index 8bb1b64622..1c4a4812e5 100644
--- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.pgm - Sources
Bundle-SymbolicName: org.eclipse.jgit.pgm.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="7.3.0.qualifier";roots="."
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 08d37278de..6bf88d9aa8 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
@@ -26,6 +26,8 @@ org.eclipse.jgit.pgm.LsTree
org.eclipse.jgit.pgm.Merge
org.eclipse.jgit.pgm.MergeBase
org.eclipse.jgit.pgm.MergeTool
+org.eclipse.jgit.pgm.MultiPackIndex
+org.eclipse.jgit.pgm.PackRefs
org.eclipse.jgit.pgm.Push
org.eclipse.jgit.pgm.ReceivePack
org.eclipse.jgit.pgm.Reflog
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index 7cd76c3011..5890ce8256 100644
--- a/org.eclipse.jgit.pgm/pom.xml
+++ b/org.eclipse.jgit.pgm/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.pgm</artifactId>
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 50ee809b98..e9630e9499 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
@@ -12,6 +12,7 @@ ARGS=ARGS
# default meta variable defined in the org.kohsuke.args4j.spi.OneArgumentOptionHandler
N=N
+addIncompatibleOptions=--update/-u cannot be combined with --all/-A/--no-ignore-removal or --no-all/--ignore-removal. Note that --renormalize implies --update.
alreadyOnBranch=Already on ''{0}''
alreadyUpToDate=Already up-to-date.
answerNo=n
@@ -255,8 +256,11 @@ unsupportedOperation=Unsupported operation: {0}
untrackedFiles=Untracked files:
updating=Updating {0}..{1}
usage_Abbrev=Instead of using the default number of hexadecimal digits (which will vary according to the number of objects in the repository with a default of 7) of the abbreviated object name, use <n> digits, or as many digits as needed to form a unique object name. An <n> of 0 will suppress long format, only showing the closest tag.
-usage_addRenormalize=Apply the "clean" process freshly to tracked files to forcibly add them again to the index. This implies -u.
+usage_addRenormalize=Apply the "clean" process freshly to tracked files to forcibly add them again to the index. This implies --update/-u.
+usage_addStageDeletions=Add, modify, or remove index entries to match the working tree. Cannot be used with --update/-u.
+usage_addDontStageDeletions=Only add or modify index entries, but do not remove index entries for which there is no file. (Don''t stage deletions.) Cannot be used with --update/-u.
usage_Aggressive=This option will cause gc to more aggressively optimize the repository at the expense of taking much more time
+usage_All=Pack all refs, except hidden refs, broken refs, and symbolic refs.
usage_AlwaysFallback=Show uniquely abbreviated commit object as fallback
usage_bareClone=Make a bare Git repository. That is, instead of creating [DIRECTORY] and placing the administrative files in [DIRECTORY]/.git, make the [DIRECTORY] itself the $GIT_DIR.
usage_extraArgument=Pass an extra argument to a merge driver. Currently supported are "-X ours" and "-X theirs".
@@ -278,6 +282,7 @@ usage_CreateAnEmptyGitRepository=Create an empty git repository
usage_Describe=Show the most recent tag that is reachable from a commit
usage_DiffAlgorithms=Test performance of jgit's diff algorithms
usage_DisplayTheVersionOfJgit=Display the version of jgit
+usage_Exclude=Do not consider tags matching the given glob(7) pattern, excluding the "refs/tags/" prefix
usage_Gc=Cleanup unnecessary files and optimize the local repository
usage_Glog=View commit history as a graph
usage_DiffGuiTool=When git-difftool is invoked with the -g or --gui option the default diff tool will be read from the configured diff.guitool variable instead of diff.tool.
@@ -299,7 +304,9 @@ usage_MakeCacheTree=Show the current cache tree structure
usage_Match=Only consider tags matching the given glob(7) pattern or patterns, excluding the "refs/tags/" prefix.
usage_MergeBase=Find as good common ancestors as possible for a merge
usage_MergesTwoDevelopmentHistories=Merges two development histories
+usage_MultiPackIndex=Operations over the multipack index
usage_PackKeptObjects=Include objects in packs locked by a ".keep" file when repacking
+usage_PackRefs=Pack heads and tags for efficient repository access
usage_PreserveOldPacks=Preserve old pack files by moving them into the preserved subdirectory instead of deleting them after repacking
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
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java
index 2ebab5e5d2..dc9d77df35 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Sasa Zivkov <sasa.zivkov@sap.com> and others
+ * Copyright (C) 2010, 2025 Sasa Zivkov <sasa.zivkov@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
@@ -16,6 +16,7 @@ import java.util.List;
import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.pgm.internal.CLIText;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
@@ -28,17 +29,33 @@ class Add extends TextBuiltin {
@Option(name = "--update", aliases = { "-u" }, usage = "usage_onlyMatchAgainstAlreadyTrackedFiles")
private boolean update = false;
- @Argument(required = true, metaVar = "metaVar_filepattern", usage = "usage_filesToAddContentFrom")
+ @Option(name = "--all", aliases = { "-A",
+ "--no-ignore-removal" }, usage = "usage_addStageDeletions")
+ private Boolean all;
+
+ @Option(name = "--no-all", aliases = {
+ "--ignore-removal" }, usage = "usage_addDontStageDeletions")
+ private void noAll(@SuppressWarnings("unused") boolean ignored) {
+ all = Boolean.FALSE;
+ }
+
+ @Argument(metaVar = "metaVar_filepattern", usage = "usage_filesToAddContentFrom")
private List<String> filepatterns = new ArrayList<>();
@Override
protected void run() throws Exception {
try (Git git = new Git(db)) {
- AddCommand addCmd = git.add();
if (renormalize) {
update = true;
}
+ if (update && all != null) {
+ throw die(CLIText.get().addIncompatibleOptions);
+ }
+ AddCommand addCmd = git.add();
addCmd.setUpdate(update).setRenormalize(renormalize);
+ if (all != null) {
+ addCmd.setAll(all.booleanValue());
+ }
for (String p : filepatterns) {
addCmd.addFilepattern(p);
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
index d2285ae64a..285fe2a96a 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
@@ -18,7 +18,7 @@ import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
import java.io.IOException;
import java.text.MessageFormat;
-import java.text.SimpleDateFormat;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -91,7 +91,7 @@ class Blame extends TextBuiltin {
private final Map<RevCommit, String> abbreviatedCommits = new HashMap<>();
- private SimpleDateFormat dateFmt;
+ private DateTimeFormatter dateFmt;
private int begin;
@@ -125,9 +125,9 @@ class Blame extends TextBuiltin {
}
if (showRawTimestamp) {
- dateFmt = new SimpleDateFormat("ZZZZ"); //$NON-NLS-1$
+ dateFmt = DateTimeFormatter.ofPattern("ZZ"); //$NON-NLS-1$
} else {
- dateFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ZZZZ"); //$NON-NLS-1$
+ dateFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss ZZ"); //$NON-NLS-1$
}
try (ObjectReader reader = db.newObjectReader();
@@ -335,12 +335,14 @@ class Blame extends TextBuiltin {
if (author == null)
return ""; //$NON-NLS-1$
- dateFmt.setTimeZone(author.getTimeZone());
- if (!showRawTimestamp)
- return dateFmt.format(author.getWhen());
+ if (!showRawTimestamp) {
+ return dateFmt.withZone(author.getZoneId())
+ .format(author.getWhenAsInstant());
+ }
return String.format("%d %s", //$NON-NLS-1$
- Long.valueOf(author.getWhen().getTime() / 1000L),
- dateFmt.format(author.getWhen()));
+ Long.valueOf(author.getWhenAsInstant().getEpochSecond()),
+ dateFmt.withZone(author.getZoneId())
+ .format(author.getWhenAsInstant()));
}
private String abbreviate(ObjectReader reader, RevCommit commit)
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java
index 52f40c2957..f5de7045d0 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java
@@ -94,7 +94,7 @@ class Config extends TextBuiltin {
if (global || isListAll())
list(SystemReader.getInstance().openUserConfig(null, fs));
if (local || isListAll())
- list(new FileBasedConfig(fs.resolve(getRepository().getDirectory(),
+ list(new FileBasedConfig(fs.resolve(getRepository().getCommonDirectory(),
Constants.CONFIG), fs));
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java
index 913d7c790d..2633336e12 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java
@@ -44,6 +44,9 @@ class Describe extends TextBuiltin {
@Option(name = "--match", usage = "usage_Match", metaVar = "metaVar_pattern")
private List<String> patterns = new ArrayList<>();
+ @Option(name = "--exclude", usage = "usage_Exclude", metaVar = "metaVar_pattern")
+ private List<String> excludes = new ArrayList<>();
+
@Option(name = "--abbrev", usage = "usage_Abbrev")
private Integer abbrev;
@@ -59,6 +62,7 @@ class Describe extends TextBuiltin {
cmd.setTags(useTags);
cmd.setAlways(always);
cmd.setMatch(patterns.toArray(new String[0]));
+ cmd.setExclude(excludes.toArray(new String[0]));
if (abbrev != null) {
cmd.setAbbrev(abbrev.intValue());
}
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 852a4b377b..958e566986 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
@@ -32,13 +32,12 @@ 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.SignatureVerifier.SignatureVerification;
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.lib.SignatureVerifiers;
import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.pgm.internal.VerificationUtils;
@@ -174,8 +173,6 @@ class Log extends RevWalkTextBuiltin {
// END -- Options shared with Diff
- private GpgSignatureVerifier verifier;
-
private GpgConfig config;
Log() {
@@ -227,9 +224,6 @@ class Log extends RevWalkTextBuiltin {
throw die(e.getMessage(), e);
} finally {
diffFmt.close();
- if (verifier != null) {
- verifier.clear();
- }
}
}
@@ -293,21 +287,13 @@ class Log extends RevWalkTextBuiltin {
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);
+ SignatureVerification verification = SignatureVerifiers.verify(db,
+ config, c);
if (verification == null) {
return;
}
VerificationUtils.writeVerification(outw, verification,
- verifier.getName(), c.getCommitterIdent());
+ verification.verifierName(), c.getCommitterIdent());
}
/**
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java
index aacde2f430..a29c4d9f36 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java
@@ -26,11 +26,6 @@ class MergeBase extends TextBuiltin {
private boolean all;
@Argument(index = 0, metaVar = "metaVar_commitish", required = true)
- void commit_0(final RevCommit c) {
- commits.add(c);
- }
-
- @Argument(index = 1, metaVar = "metaVar_commitish", required = true)
private List<RevCommit> commits = new ArrayList<>();
@Override
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java
new file mode 100644
index 0000000000..1844223cc9
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2025, Google LLC.
+ *
+ * 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;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
+import org.eclipse.jgit.internal.storage.file.Pack;
+import org.eclipse.jgit.internal.storage.file.PackFile;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndexPrettyPrinter;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndexWriter;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+@Command(common = true, usage = "usage_MultiPackIndex")
+@SuppressWarnings("nls")
+class MultiPackIndex extends TextBuiltin {
+ @Argument(index = 0, required = true, usage = "write, print")
+ private String command;
+
+ @Option(name = "--midx")
+ private String midxPath;
+
+ /** {@inheritDoc} */
+ @Override
+ protected void run() throws IOException {
+ switch (command) {
+ case "print":
+ printMultiPackIndex();
+ break;
+ case "write":
+ writeMultiPackIndex();
+ break;
+ default:
+ outw.println("Unknown command " + command);
+ }
+ }
+
+ private void printMultiPackIndex() {
+ if (midxPath == null || midxPath.isEmpty()) {
+ throw die("'print' requires the path of a multipack "
+ + "index file with --midx option.");
+ }
+
+ try (FileInputStream is = new FileInputStream(midxPath)) {
+ PrintWriter pw = new PrintWriter(outw, true);
+ MultiPackIndexPrettyPrinter.prettyPrint(is.readAllBytes(), pw);
+ } catch (FileNotFoundException e) {
+ throw die(true, e);
+ } catch (IOException e) {
+ throw die(true, e);
+ }
+ }
+
+ private void writeMultiPackIndex() throws IOException {
+ if (!(db.getObjectDatabase() instanceof ObjectDirectory)) {
+ throw die("This repository object db doesn't have packs");
+ }
+
+ File midx;
+ if (midxPath == null || midxPath.isEmpty()) {
+ midx = new File(((ObjectDirectory) db.getObjectDatabase())
+ .getPackDirectory(), "multi-pack-index");
+ } else {
+ midx = new File(midxPath);
+ }
+
+ errw.println("Writing " + midx.getAbsolutePath());
+
+ ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase();
+
+ Map<String, PackIndex> indexes = new HashMap<>();
+ for (Pack pack : odb.getPacks()) {
+ PackFile packFile = pack.getPackFile().create(PackExt.INDEX);
+ try {
+ indexes.put(packFile.getName(), pack.getIndex());
+ } catch (IOException e) {
+ throw die("Cannot open index in pack", e);
+ }
+ }
+
+ MultiPackIndexWriter writer = new MultiPackIndexWriter();
+ try (FileOutputStream out = new FileOutputStream(midxPath)) {
+ writer.write(NullProgressMonitor.INSTANCE, out, indexes);
+ } catch (IOException e) {
+ throw die("Cannot write midx " + midxPath, e);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/PackRefs.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/PackRefs.java
new file mode 100644
index 0000000000..ee05f5ca0b
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/PackRefs.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.pgm;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.kohsuke.args4j.Option;
+
+@Command(common = true, usage = "usage_PackRefs")
+class PackRefs extends TextBuiltin {
+ @Option(name = "--all", usage = "usage_All")
+ private boolean all;
+
+ @Override
+ protected void run() {
+ Git git = Git.wrap(db);
+ try {
+ git.packRefs().setProgressMonitor(new TextProgressMonitor(errw))
+ .setAll(all).call();
+ } catch (GitAPIException e) {
+ throw die(e.getMessage(), e);
+ }
+ }
+}
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 4feb090032..a3a6782a71 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
@@ -14,11 +14,10 @@ package org.eclipse.jgit.pgm;
import java.io.BufferedOutputStream;
import java.io.IOException;
-import java.text.DateFormat;
import java.text.MessageFormat;
-import java.text.SimpleDateFormat;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import java.util.Locale;
-import java.util.TimeZone;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.diff.RawTextComparator;
@@ -30,12 +29,11 @@ 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.lib.SignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.SignatureVerifiers;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.pgm.internal.VerificationUtils;
import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler;
@@ -52,9 +50,9 @@ import org.kohsuke.args4j.Option;
@Command(common = true, usage = "usage_show")
class Show extends TextBuiltin {
- private final TimeZone myTZ = TimeZone.getDefault();
+ private final ZoneId myTZ = ZoneId.systemDefault();
- private final DateFormat fmt;
+ private final DateTimeFormatter fmt;
private DiffFormatter diffFmt;
@@ -158,7 +156,8 @@ class Show extends TextBuiltin {
// END -- Options shared with Diff
Show() {
- fmt = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy ZZZZZ", Locale.US); //$NON-NLS-1$
+ fmt = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy ZZ", //$NON-NLS-1$
+ Locale.US);
}
@Override
@@ -233,15 +232,17 @@ class Show extends TextBuiltin {
outw.print(tag.getTagName());
outw.println();
- final PersonIdent tagger = tag.getTaggerIdent();
+ PersonIdent tagger = tag.getTaggerIdent();
if (tagger != null) {
outw.println(MessageFormat.format(CLIText.get().taggerInfo,
tagger.getName(), tagger.getEmailAddress()));
- final TimeZone taggerTZ = tagger.getTimeZone();
- fmt.setTimeZone(taggerTZ != null ? taggerTZ : myTZ);
+ ZoneId taggerTZ = tagger.getZoneId();
+ String formattedTaggerTime = fmt
+ .withZone(taggerTZ != null ? taggerTZ : myTZ)
+ .format(tagger.getWhenAsInstant());
outw.println(MessageFormat.format(CLIText.get().dateInfo,
- fmt.format(tagger.getWhen())));
+ formattedTaggerTime));
}
outw.println();
@@ -294,10 +295,12 @@ class Show extends TextBuiltin {
outw.println(MessageFormat.format(CLIText.get().authorInfo,
author.getName(), author.getEmailAddress()));
- final TimeZone authorTZ = author.getTimeZone();
- fmt.setTimeZone(authorTZ != null ? authorTZ : myTZ);
+ final ZoneId authorTZ = author.getZoneId();
+ String formattedAuthorTime = fmt
+ .withZone(authorTZ != null ? authorTZ : myTZ)
+ .format(author.getWhenAsInstant());
outw.println(MessageFormat.format(CLIText.get().dateInfo,
- fmt.format(author.getWhen())));
+ formattedAuthorTime));
outw.println();
final String[] lines = c.getFullMessage().split("\n"); //$NON-NLS-1$
@@ -335,23 +338,13 @@ class Show extends TextBuiltin {
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();
+ SignatureVerification verification = SignatureVerifiers.verify(db,
+ config, c);
+ if (verification == null) {
+ throw die(CLIText.get().logNoSignatureVerifier, null);
}
+ VerificationUtils.writeVerification(outw, verification,
+ verification.verifierName(), c.getCommitterIdent());
}
}
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 4ea67ab92c..6be30c9447 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
@@ -27,10 +27,10 @@ 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.lib.SignatureVerifier.SignatureVerification;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.pgm.internal.VerificationUtils;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -106,7 +106,8 @@ class Tag extends TextBuiltin {
if (error != null) {
throw die(error.getMessage(), error);
}
- writeVerification(verifySig.getVerifier().getName(),
+ writeVerification(
+ verification.getVerification().verifierName(),
(RevTag) verification.getObject(),
verification.getVerification());
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
index 2f96ef7d57..22d9e3440a 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
@@ -18,8 +18,8 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.MessageFormat;
+import java.time.Instant;
import java.util.ArrayList;
-import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
@@ -166,7 +166,8 @@ class RebuildCommitGraph extends TextBuiltin {
final CommitBuilder newc = new CommitBuilder();
newc.setTreeId(emptyTree);
- newc.setAuthor(new PersonIdent(me, new Date(t.commitTime)));
+ newc.setAuthor(new PersonIdent(me,
+ Instant.ofEpochSecond(t.commitTime)));
newc.setCommitter(newc.getAuthor());
newc.setParentIds(newParents);
newc.setMessage("ORIGINAL " + t.oldId.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
index c95f1384e8..74e322ff7f 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
@@ -31,6 +31,7 @@ import org.eclipse.jgit.pgm.Command;
import org.eclipse.jgit.pgm.TextBuiltin;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.util.TemporaryBuffer;
import org.kohsuke.args4j.Argument;
@@ -68,7 +69,7 @@ class ShowPackDelta extends TextBuiltin {
ObjectReuseAsIs asis = (ObjectReuseAsIs) reader;
ObjectToPack target = asis.newObjectToPack(obj, obj.getType());
- PackWriter pw = new PackWriter(reader) {
+ PackWriter pw = new PackWriter(new PackConfig(), reader) {
@Override
public void select(ObjectToPack otp, StoredObjectRepresentation next) {
otp.select(next);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java
index faa2bceb74..7aff2dd9cd 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java
@@ -24,6 +24,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -209,14 +211,15 @@ class WriteReftable extends TextBuiltin {
}
String ref = m.group(1);
double t = Double.parseDouble(m.group(2));
- long time = ((long) t) * 1000L;
+ Instant time = Instant.ofEpochSecond((long) t);
long index = (long) (t * 1e6);
String user = m.group(3);
ObjectId oldId = parseId(m.group(4));
ObjectId newId = parseId(m.group(5));
String msg = m.group(6);
String email = user + "@gerrit"; //$NON-NLS-1$
- PersonIdent who = new PersonIdent(user, email, time, -480);
+ PersonIdent who = new PersonIdent(user, email, time,
+ ZoneOffset.ofHours(-8));
log.add(new LogEntry(ref, index, who, oldId, newId, msg));
}
}
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 b5bf6d2bc3..bb1e950542 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, 2021 Obeo and others
+ * Copyright (C) 2013, 2025 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
@@ -91,6 +91,7 @@ public class CLIText extends TranslationBundle {
}
// @formatter:off
+ /***/ public String addIncompatibleOptions;
/***/ public String alreadyOnBranch;
/***/ public String alreadyUpToDate;
/***/ public String answerNo;
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
index c1f8a86a8c..64ee602620 100644
--- 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
@@ -11,7 +11,7 @@ package org.eclipse.jgit.pgm.internal;
import java.io.IOException;
-import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.SignatureVerifier.SignatureVerification;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.util.GitDateFormatter;
import org.eclipse.jgit.util.SignatureUtils;
diff --git a/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
index ad0164086a..8942a417a9 100644
--- a/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
@@ -2,16 +2,16 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent;singleton:=true
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/agent
Bundle-Vendor: %Bundle-Vendor
-Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[7.0.0,7.1.0)"
+Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[7.3.0,7.4.0)"
Bundle-ActivationPolicy: lazy
Automatic-Module-Name: org.eclipse.jgit.ssh.apache.agent
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Import-Package: org.eclipse.jgit.transport.sshd;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)"
+Import-Package: org.eclipse.jgit.transport.sshd;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)"
Require-Bundle: com.sun.jna;bundle-version="[5.8.0,6.0.0)",
com.sun.jna.platform;bundle-version="[5.8.0,6.0.0)"
-Export-Package: org.eclipse.jgit.internal.transport.sshd.agent.connector;version="7.0.0";x-internal:=true
+Export-Package: org.eclipse.jgit.internal.transport.sshd.agent.connector;version="7.3.0";x-internal:=true
diff --git a/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF
index 684ab70f86..37b442e8bb 100644
--- a/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.ssh.apache.agent - Sources
Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache.agent/pom.xml b/org.eclipse.jgit.ssh.apache.agent/pom.xml
index f407ce11bf..2d34495b72 100644
--- a/org.eclipse.jgit.ssh.apache.agent/pom.xml
+++ b/org.eclipse.jgit.ssh.apache.agent/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ssh.apache.agent</artifactId>
diff --git a/org.eclipse.jgit.ssh.apache.test/.classpath b/org.eclipse.jgit.ssh.apache.test/.classpath
index 6fdb99a4b2..5be47afffb 100644
--- a/org.eclipse.jgit.ssh.apache.test/.classpath
+++ b/org.eclipse.jgit.ssh.apache.test/.classpath
@@ -11,5 +11,10 @@
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
+ <classpathentry kind="src" path="tst-rsrc">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.ssh.apache.test/.gitattributes b/org.eclipse.jgit.ssh.apache.test/.gitattributes
new file mode 100644
index 0000000000..b5b937561d
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.gitattributes
@@ -0,0 +1,2 @@
+/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/repo.bundle binary
+/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl* binary
diff --git a/org.eclipse.jgit.ssh.apache.test/BUILD b/org.eclipse.jgit.ssh.apache.test/BUILD
index b384464484..bf796c058e 100644
--- a/org.eclipse.jgit.ssh.apache.test/BUILD
+++ b/org.eclipse.jgit.ssh.apache.test/BUILD
@@ -1,19 +1,51 @@
load(
+ "@com_googlesource_gerrit_bazlets//tools:genrule2.bzl",
+ "genrule2",
+)
+load(
"@com_googlesource_gerrit_bazlets//tools:junit.bzl",
"junit_tests",
)
+DEPS = [
+ "//lib:bcpkix",
+ "//lib:bcprov",
+ "//lib:bcutil",
+ "//lib:junit",
+ "//lib:slf4j-api",
+ "//lib:sshd-osgi",
+ "//lib:sshd-sftp",
+ "//org.eclipse.jgit:jgit",
+ "//org.eclipse.jgit.junit:junit",
+ "//org.eclipse.jgit.junit.ssh:junit-ssh",
+ "//org.eclipse.jgit.ssh.apache:ssh-apache",
+]
+
+HELPERS = ["tst/org/eclipse/jgit/internal/signing/ssh/AbstractSshSignatureTest.java"]
+
junit_tests(
name = "sshd_apache",
- srcs = glob(["tst/**/*.java"]),
+ srcs = glob(
+ ["tst/**/*.java"],
+ exclude = HELPERS,
+ ),
tags = ["sshd"],
- deps = [
- "//lib:eddsa",
- "//lib:junit",
- "//lib:sshd-osgi",
- "//lib:sshd-sftp",
- "//org.eclipse.jgit:jgit",
- "//org.eclipse.jgit.junit.ssh:junit-ssh",
- "//org.eclipse.jgit.ssh.apache:ssh-apache",
+ runtime_deps = [":tst_rsrc"],
+ deps = DEPS + [
+ ":helpers",
],
)
+
+java_library(
+ name = "helpers",
+ testonly = 1,
+ srcs = HELPERS,
+ deps = DEPS,
+)
+
+genrule2(
+ name = "tst_rsrc",
+ srcs = glob(["tst-rsrc/**"]),
+ outs = ["tst_rsrc.jar"],
+ cmd = "tar cf - $(SRCS) | tar -C $$TMP --strip-components=2 -xf - && cd $$TMP && zip -qr $$ROOT/$@ .",
+)
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 54d610aa91..88ab277cd2 100644
--- a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
@@ -3,35 +3,47 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.ssh.apache.test
Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
Require-Bundle: org.hamcrest.core;bundle-version="[1.3.0,2.0.0)"
-Import-Package: org.apache.sshd.client.config.hosts;version="[2.12.0,2.13.0)",
- org.apache.sshd.common;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.auth;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.config.keys;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.helpers;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.kex;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.keyprovider;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.session;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.signature;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.net;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.security;version="[2.12.0,2.13.0)",
- org.apache.sshd.core;version="[2.12.0,2.13.0)",
- org.apache.sshd.server;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.forward;version="[2.12.0,2.13.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.sshd.proxy;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit.ssh;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.sshd;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.sshd.agent;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+Import-Package: org.apache.sshd.certificate;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.config.hosts;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.auth;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.cipher;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.config.keys;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.helpers;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.kex;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.keyprovider;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.session;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.signature;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.buffer;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.net;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.security;version="[2.15.0,2.16.0)",
+ org.apache.sshd.core;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.forward;version="[2.15.0,2.16.0)",
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.signing.ssh;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.sshd;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit.ssh;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.sshd;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.sshd.agent;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.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)"
+ org.junit.rules;version="[4.13.0,5.0.0)",
+ org.junit.runner;version="[4.13,5.0.0)",
+ org.junit.runners;version="[4.13.0,5.0.0)",
+ org.slf4j;version="[1.7.0,3.0.0)"
diff --git a/org.eclipse.jgit.ssh.apache.test/pom.xml b/org.eclipse.jgit.ssh.apache.test/pom.xml
index 967b781d10..b86a56091c 100644
--- a/org.eclipse.jgit.ssh.apache.test/pom.xml
+++ b/org.eclipse.jgit.ssh.apache.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
@@ -91,6 +91,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.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/allowed_signers b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/allowed_signers
new file mode 100644
index 0000000000..ec74409e54
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/allowed_signers
@@ -0,0 +1,2 @@
+tester@example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO
+*@example.com cert-authority ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMdEl+iOTEbf1RC3uicECtid+SaIMsAw7wrlWhOQTyBV
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key
new file mode 100644
index 0000000000..b8de8c3353
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDHRJfojkxG39UQt7onBArYnfkmiDLAMO8K5VoTkE8gVQAAAJAhCMgzIQjI
+MwAAAAtzc2gtZWQyNTUxOQAAACDHRJfojkxG39UQt7onBArYnfkmiDLAMO8K5VoTkE8gVQ
+AAAEBmcXpast20+B4IzA0Xex2CKYiiWJj3NFJ5F0kil113vcdEl+iOTEbf1RC3uicECtid
++SaIMsAw7wrlWhOQTyBVAAAADVRIV09AU0VBR044MDA=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key.pub
new file mode 100644
index 0000000000..842415b0e8
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMdEl+iOTEbf1RC3uicECtid+SaIMsAw7wrlWhOQTyBV
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2
new file mode 100644
index 0000000000..a4af0479bc
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACA7S4ycIB6oTx4UN8l9N+u016UgMzkrbT7E+2XbG35jgwAAAJAa2jfBGto3
+wQAAAAtzc2gtZWQyNTUxOQAAACA7S4ycIB6oTx4UN8l9N+u016UgMzkrbT7E+2XbG35jgw
+AAAEBothGMqFaA5aTO8MLx9wm1oDRfzQCSsu7uJwrOiUFTTTtLjJwgHqhPHhQ3yX0367TX
+pSAzOSttPsT7ZdsbfmODAAAADVRIV09AU0VBR044MDA=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2.pub
new file mode 100644
index 0000000000..e46c87e83f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDtLjJwgHqhPHhQ3yX0367TXpSAzOSttPsT7ZdsbfmOD
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/expired.cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/expired.cert
new file mode 100644
index 0000000000..9da63ec900
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/expired.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAINFZ5NKywAWh1G1P6BiBKArmYKs1BDhJBOawJKlS29VXAAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzOAAAAAAAAAAEAAAABAAAADGV4cGlyZWRfY2VydAAAAAAAAAAAZtOugAAAAABm1QAAAAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgx0SX6I5MRt/VELe6JwQK2J35JogywDDvCuVaE5BPIFUAAABTAAAAC3NzaC1lZDI1NTE5AAAAQNf8i5dhRqWRe06epIRrZ5V+QZHq3ZrlJtlx98UJya9GAeCrJ5oHwBjr5O5TL5wNJS5Hz+T1qsJNFU9d1wdcuwI=
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/no_principals.cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/no_principals.cert
new file mode 100644
index 0000000000..101e37469d
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/no_principals.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAILzuED1RSloB/enTghTEKSACVOuEARP0f8UVXSRwEXN6AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzOAAAAAAAAAAIAAAABAAAADW5vX3ByaW5jaXBhbHMAAAAAAAAAAGbTroAAAAAAZyLIgAAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIMdEl+iOTEbf1RC3uicECtid+SaIMsAw7wrlWhOQTyBVAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEBwEQ2D0OHn4QDHnsINlgWUWpmhukseQCJu3Adulz28fFtewp1LLqkBy50wR6vJe1ifYbY4hzReXOSyoTmHSXEN
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other-ca.cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other-ca.cert
new file mode 100644
index 0000000000..752fee1778
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other-ca.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIHNW2bzSS61lvgHippv3Ymx4cVEAXBVCb8lFXHnVpsSyAAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzOAAAAAAAAAAYAAAABAAAAB3Rlc3RlcjIAAAAWAAAAEnRlc3RlckBleGFtcGxlLmNvbQAAAABm066AAAAAAGciyIAAAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACA7S4ycIB6oTx4UN8l9N+u016UgMzkrbT7E+2XbG35jgwAAAFMAAAALc3NoLWVkMjU1MTkAAABAuJ8zBazcaYTbUEr9QtoYox0MkVBg+8LANxJxc885M2vmg9yPHpTfV/emupqhBwuYcPJSskTxl7WX4TUNvhMsAA==
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other.cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other.cert
new file mode 100644
index 0000000000..15825f6055
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGKXzyrvDzj9ObQ4SuzqytK6nomOV8DhgdzODfWuup1sAAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzOAAAAAAAAAAQAAAABAAAABW90aGVyAAAAFQAAABFvdGhlckBleGFtcGxlLmNvbQAAAABm066AAAAAAGciyIAAAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACDHRJfojkxG39UQt7onBArYnfkmiDLAMO8K5VoTkE8gVQAAAFMAAAALc3NoLWVkMjU1MTkAAABA1ycFqWehyC6pIISEkXSTtHbatLWl9HHAoUFouQiDdubAnMDRSkyHipXR62rq+8yEAvtqm1mXBzO8nLalkF9xAA==
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/tester.cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/tester.cert
new file mode 100644
index 0000000000..a2b241c241
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/tester.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAICSl1xsyTWb23YlKo21musxOzj4L4eD2coTkHbBw2uOyAAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzOAAAAAAAAAAMAAAABAAAABnRlc3RlcgAAABYAAAASdGVzdGVyQGV4YW1wbGUuY29tAAAAAGbTroAAAAAAZyLIgAAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIMdEl+iOTEbf1RC3uicECtid+SaIMsAw7wrlWhOQTyBVAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDyjzq/0Egm1OxwrvqPZKUihE3w357Ji9Nd3j7VnUuvSYTXAdB9P0E+a2hyCcemmsil1MsvWTiCSSOsrHVB6FEO
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/two_principals.cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/two_principals.cert
new file mode 100644
index 0000000000..5f7164a7fc
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/two_principals.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIFmWKr9gNSQT0vna7k3uOyUF9CTcMGw2zxTFBf2Ev8TzAAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzOAAAAAAAAAAgAAAABAAAABnRlc3RlcgAAACkAAAAPZm9vQGV4YW1wbGUuY29tAAAAEnRlc3RlckBleGFtcGxlLmNvbQAAAABm066AAAAAAGciyIAAAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACDHRJfojkxG39UQt7onBArYnfkmiDLAMO8K5VoTkE8gVQAAAFMAAAALc3NoLWVkMjU1MTkAAABAqlSX2GzLz5U+hN/gF9UUyAkE6h5BgVFYhsyf1MR/B7Hoxa29wGLbJpUplrqEHMxoud2zfH2Nhj00unc3lr5bBA== ./signing_key.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl
new file mode 100644
index 0000000000..9469340ed3
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-all b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-all
new file mode 100644
index 0000000000..6f744c3a2d
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-all
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-ca b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-ca
new file mode 100644
index 0000000000..84a8bc6b38
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-ca
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-cert
new file mode 100644
index 0000000000..26f29b2ba5
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-cert
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-empty b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-empty
new file mode 100644
index 0000000000..78e5187f29
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-empty
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-hash b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-hash
new file mode 100644
index 0000000000..cdd1351598
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-hash
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid
new file mode 100644
index 0000000000..1a65243f44
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid-wild b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid-wild
new file mode 100644
index 0000000000..9ba549f363
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid-wild
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keys b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keys
new file mode 100644
index 0000000000..8dd496d7c1
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keys
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial
new file mode 100644
index 0000000000..9965e2e36f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial-wild b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial-wild
new file mode 100644
index 0000000000..aefd2b1a84
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial-wild
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha1 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha1
new file mode 100644
index 0000000000..3928543ad1
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha1
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha256 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha256
new file mode 100644
index 0000000000..cdd1351598
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha256
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-text b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-text
new file mode 100644
index 0000000000..77ddd5e0d0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-text
@@ -0,0 +1,11 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC29QZ72HtyCdaNo6p2GH3fJpUynwkvs8Acwn66G7YTh
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0ZC4fqgbBgROeX1sOEPr4uMVNfPdJ62bVo/zvSMRQx
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG5IH9T7FY47VJDUoyOlB/iqCN4pO8dgOrxclmKN5R5w
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINjhG4+EnQy8YHLsfE8+IQwNWZVn1GBYX75pwxBCZGmy
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXm+qgTs+sO+9zvoZBxkQD39R2rQqQCVezxQoGjKui5
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL8+aQKja+SbqxfWR61FCcsbBw2jaF/KHvcqdP2Fbp6Q
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAOL7IULalT2Izo9TgRf1t2HNpZ5WCZJH5oRCd9LK3BN
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGVaSedhPkl2Hrx1nOOKT2E52ADsBebawws87NN1+P6e
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICxyaxj7pRVDeh/gxJem9BLhoUQKGnKXHfDrB/GtC1KB
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID96GKtj4wN+OwvrQsgP37fQVUXThCML796qqFNLVDCA
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEh3BFcTzXmg1Fi5LvWiUDWORsHzVhUCm8ekrEJG6+6A
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001
new file mode 100644
index 0000000000..893fd5e776
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAtvUGe9h7cgnWjaOqdhh93yaVMp8JL7PAHMJ+uhu2E4QAAAIhUa4KiVGuC
+ogAAAAtzc2gtZWQyNTUxOQAAACAtvUGe9h7cgnWjaOqdhh93yaVMp8JL7PAHMJ+uhu2E4Q
+AAAECKgy+3FBgpdfxjOtNy9TamhadMWSyPlPiwu06mYVReyS29QZ72HtyCdaNo6p2GH3fJ
+pUynwkvs8Acwn66G7YThAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001-cert.pub
new file mode 100644
index 0000000000..e2bcd25964
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIE1UUsQ+sncsuST6eGe3B5Se7purqhGcWrkyIwUnQM/jAAAAIC29QZ72HtyCdaNo6p2GH3fJpUynwkvs8Acwn66G7YThAAAAAAAAAAEAAAABAAAACXJldm9rZWQgMQAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgFtF5l68spzOFrIpQANF0C9nkNdXMAmlCRwHgw91C784AAABTAAAAC3NzaC1lZDI1NTE5AAAAQCjDATJVQs3odl9fsqaxyx/18qrodZEDyYZAsdqg0GMx8CvLYt4xHENyVm7kyBRxOeh3EKfII0WFoYCV4mGZ/wU= ./tst-keys/revoked-0001.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001.pub
new file mode 100644
index 0000000000..f561982278
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC29QZ72HtyCdaNo6p2GH3fJpUynwkvs8Acwn66G7YTh
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004
new file mode 100644
index 0000000000..e50a4fefe9
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACC9GQuH6oGwYETnl9bDhD6+LjFTXz3Setm1aP870jEUMQAAAIiQzZzikM2c
+4gAAAAtzc2gtZWQyNTUxOQAAACC9GQuH6oGwYETnl9bDhD6+LjFTXz3Setm1aP870jEUMQ
+AAAEBpn5dxbvHhqAsSVN3IqRwzbFFgOhdmpkOP+nvoKq+rSr0ZC4fqgbBgROeX1sOEPr4u
+MVNfPdJ62bVo/zvSMRQxAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004-cert.pub
new file mode 100644
index 0000000000..8e92fa7c86
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIC5jMLPDlEVbyPU/Icb04BF5jxN+OT8kpuO5c0CV6/AYAAAAIL0ZC4fqgbBgROeX1sOEPr4uMVNfPdJ62bVo/zvSMRQxAAAAAAAAAAQAAAABAAAACXJldm9rZWQgNAAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgFtF5l68spzOFrIpQANF0C9nkNdXMAmlCRwHgw91C784AAABTAAAAC3NzaC1lZDI1NTE5AAAAQOH4yNn7+zyvsCV8BCoop5xYv4uFk27VZRjmscuy3J66KNwLay9XkvkRNArDaWBwH47dmkcU7F6fLLpY4vN2jgM= ./tst-keys/revoked-0004.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004.pub
new file mode 100644
index 0000000000..1d7fe7fa7f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0ZC4fqgbBgROeX1sOEPr4uMVNfPdJ62bVo/zvSMRQx
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010
new file mode 100644
index 0000000000..fb457df249
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBuSB/U+xWOO1SQ1KMjpQf4qgjeKTvHYDq8XJZijeUecAAAAIgvtSiML7Uo
+jAAAAAtzc2gtZWQyNTUxOQAAACBuSB/U+xWOO1SQ1KMjpQf4qgjeKTvHYDq8XJZijeUecA
+AAAECI2si7/SGjMM1UyhrFPXx4laQIfFUsb1+yfXKwQyeOXW5IH9T7FY47VJDUoyOlB/iq
+CN4pO8dgOrxclmKN5R5wAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010-cert.pub
new file mode 100644
index 0000000000..9492f88a90
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIN2arXaBzVIdxAFfU+XU1Uc788HKlDH3tOLdDtcoORLmAAAAIG5IH9T7FY47VJDUoyOlB/iqCN4pO8dgOrxclmKN5R5wAAAAAAAAAAoAAAABAAAACnJldm9rZWQgMTAAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDwhgQsYOG/eKf8EfH+fAmEW+88/ZJCmxAExEFPxkGL59waZcGiOJqquTKiqN5Kod8hpUrvZywrA0tjrRkYw8wH ./tst-keys/revoked-0010.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010.pub
new file mode 100644
index 0000000000..37a0d84e7a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG5IH9T7FY47VJDUoyOlB/iqCN4pO8dgOrxclmKN5R5w
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050
new file mode 100644
index 0000000000..b02e9df821
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDY4RuPhJ0MvGBy7HxPPiEMDVmVZ9RgWF++acMQQmRpsgAAAIgCZLe5AmS3
+uQAAAAtzc2gtZWQyNTUxOQAAACDY4RuPhJ0MvGBy7HxPPiEMDVmVZ9RgWF++acMQQmRpsg
+AAAEB9Q6rpWK04mQDoeKSB2I7p/rb8pu00ClhR+vRATl4TYdjhG4+EnQy8YHLsfE8+IQwN
+WZVn1GBYX75pwxBCZGmyAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050-cert.pub
new file mode 100644
index 0000000000..90bb86f9b5
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIIecNj2Es6VfyCrhol4swP9lutvphd3seh+/b2LpD0EsAAAAINjhG4+EnQy8YHLsfE8+IQwNWZVn1GBYX75pwxBCZGmyAAAAAAAAADIAAAABAAAACnJldm9rZWQgNTAAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEA2q8tCXV8FXkB0QWnFNWfCL7zz5jCXL9ZQADM1DaGi8oUU/dxmlQtWgMxuu5vNuvOYQGPDcBLj+by8VqAdvZMP ./tst-keys/revoked-0050.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050.pub
new file mode 100644
index 0000000000..f3ad249daa
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINjhG4+EnQy8YHLsfE8+IQwNWZVn1GBYX75pwxBCZGmy
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090
new file mode 100644
index 0000000000..efa3d5ef0a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACCV5vqoE7PrDvvc76GQcZEA9/Udq0KkAlXs8UKBoyrouQAAAIg3mgznN5oM
+5wAAAAtzc2gtZWQyNTUxOQAAACCV5vqoE7PrDvvc76GQcZEA9/Udq0KkAlXs8UKBoyrouQ
+AAAEAkRynGUH9n5hcp/S1WALvuIEDtbkMi2A7yNWze0o4gWpXm+qgTs+sO+9zvoZBxkQD3
+9R2rQqQCVezxQoGjKui5AAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090-cert.pub
new file mode 100644
index 0000000000..26e61e0abb
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIOjIztpPiaKY0hztHWtWpX+4LEoyy8qYPPT277K3bykSAAAAIJXm+qgTs+sO+9zvoZBxkQD39R2rQqQCVezxQoGjKui5AAAAAAAAAFoAAAABAAAACnJldm9rZWQgOTAAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEBUaAWyv/jZrbrCO5zw2HuZcWYBig8R2jdvkKr5yzWMWEVRtn97gnAUsIGxkgUnUAs3B2En2FH2NaicC1F1n3sF ./tst-keys/revoked-0090.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090.pub
new file mode 100644
index 0000000000..e51b88c268
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXm+qgTs+sO+9zvoZBxkQD39R2rQqQCVezxQoGjKui5
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500
new file mode 100644
index 0000000000..900d444363
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACC/PmkCo2vkm6sX1ketRQnLGwcNo2hfyh73KnT9hW6ekAAAAIhDam0PQ2pt
+DwAAAAtzc2gtZWQyNTUxOQAAACC/PmkCo2vkm6sX1ketRQnLGwcNo2hfyh73KnT9hW6ekA
+AAAED606GrYWlY7TOXcr8vAr3fjMtCtetdpwFHi2pzgf2Bbb8+aQKja+SbqxfWR61FCcsb
+Bw2jaF/KHvcqdP2Fbp6QAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500-cert.pub
new file mode 100644
index 0000000000..07096182b4
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIIEblAg4b1eJ5KnT7KvYoOfe24La+nAKKLIYdsR6CdreAAAAIL8+aQKja+SbqxfWR61FCcsbBw2jaF/KHvcqdP2Fbp6QAAAAAAAAAfQAAAABAAAAC3Jldm9rZWQgNTAwAAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABAc0WEuRfi9LG9uTfKY4Dh5MJCHUG7Dqp1J4S4Gs1iOzFX2YKgYXc0O+9j3jJ5/fB4z960Y1AxYR4TWEo1pNjzBQ== ./tst-keys/revoked-0500.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500.pub
new file mode 100644
index 0000000000..13d1aa4238
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL8+aQKja+SbqxfWR61FCcsbBw2jaF/KHvcqdP2Fbp6Q
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510
new file mode 100644
index 0000000000..a58675ec55
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACADi+yFC2pU9iM6PU4EX9bdhzaWeVgmSR+aEQnfSytwTQAAAIgigF2AIoBd
+gAAAAAtzc2gtZWQyNTUxOQAAACADi+yFC2pU9iM6PU4EX9bdhzaWeVgmSR+aEQnfSytwTQ
+AAAEBWpyFpK0a+cdNPFMsvHTHtjBJpX4aMHxBAcEPN8hnpWAOL7IULalT2Izo9TgRf1t2H
+NpZ5WCZJH5oRCd9LK3BNAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510-cert.pub
new file mode 100644
index 0000000000..1431af306f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAII8u8ho0YtDyXWYKv4WeOXSaRUxU8sUV0dQujB2J9VLaAAAAIAOL7IULalT2Izo9TgRf1t2HNpZ5WCZJH5oRCd9LK3BNAAAAAAAAAf4AAAABAAAAC3Jldm9rZWQgNTEwAAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABA3aijJnt8mJ8vLtr7H2PBVJHtNJpL6MQZNXHC6svzygIqZwEq3tDHGR00TPHaCYAqDEXQZysONciOQtQHzKXuBw== ./tst-keys/revoked-0510.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510.pub
new file mode 100644
index 0000000000..33ad644ab3
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAOL7IULalT2Izo9TgRf1t2HNpZ5WCZJH5oRCd9LK3BN
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520
new file mode 100644
index 0000000000..630316c0ae
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBlWknnYT5Jdh68dZzjik9hOdgA7AXm2sMLPOzTdfj+ngAAAIghEm1OIRJt
+TgAAAAtzc2gtZWQyNTUxOQAAACBlWknnYT5Jdh68dZzjik9hOdgA7AXm2sMLPOzTdfj+ng
+AAAEDfVYURudvfzK3ZFx6T2O1CWi0emOZ0MYPcDzUVlu1WmGVaSedhPkl2Hrx1nOOKT2E5
+2ADsBebawws87NN1+P6eAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520-cert.pub
new file mode 100644
index 0000000000..b2909431ad
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAID/r9T2Sv0NGmlcHl6Fw8rVPIupmsqwq3WAG1NvW7WRcAAAAIGVaSedhPkl2Hrx1nOOKT2E52ADsBebawws87NN1+P6eAAAAAAAAAggAAAABAAAAC3Jldm9rZWQgNTIwAAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABAF8zkeAqwtlxF4iy4mDEHkzVaRqcS0sZ57gcZBWGn/peGFy3MpSxlFQM/IC2pNZ7GuCVSIPV6rRLJC65YMMOEDQ== ./tst-keys/revoked-0520.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520.pub
new file mode 100644
index 0000000000..fc13d37d33
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGVaSedhPkl2Hrx1nOOKT2E52ADsBebawws87NN1+P6e
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550
new file mode 100644
index 0000000000..5e671b4ee0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAscmsY+6UVQ3of4MSXpvQS4aFEChpylx3w6wfxrQtSgQAAAIj/9GKZ//Ri
+mQAAAAtzc2gtZWQyNTUxOQAAACAscmsY+6UVQ3of4MSXpvQS4aFEChpylx3w6wfxrQtSgQ
+AAAEDKC3eEgvCMy86rktq7VU1YQjjKY1iDFPVxWgKKcGJKkyxyaxj7pRVDeh/gxJem9BLh
+oUQKGnKXHfDrB/GtC1KBAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550-cert.pub
new file mode 100644
index 0000000000..f529a91713
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIF9q+Cg+9DSKt09eW1NXqVC4dZ3v80sZIYtc0/yqHRb+AAAAICxyaxj7pRVDeh/gxJem9BLhoUQKGnKXHfDrB/GtC1KBAAAAAAAAAiYAAAABAAAAC3Jldm9rZWQgNTUwAAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABAovTuFOXLNCc4hQcI2hatXe2hbBQYbcnUo2BNdJ9EvIOsH/T0DzzEfRQajMQ+QD6oujIx7fb1Z2sRVPOAb3AcBg== ./tst-keys/revoked-0550.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550.pub
new file mode 100644
index 0000000000..e09316a37e
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICxyaxj7pRVDeh/gxJem9BLhoUQKGnKXHfDrB/GtC1KB
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799
new file mode 100644
index 0000000000..8edd73662b
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACA/ehirY+MDfjsL60LID9+30FVF04QjC+/eqqhTS1QwgAAAAIjdntzR3Z7c
+0QAAAAtzc2gtZWQyNTUxOQAAACA/ehirY+MDfjsL60LID9+30FVF04QjC+/eqqhTS1QwgA
+AAAEDQEb+IFCIz+yvkhmrOQ85GafOm9ra0oNRontpox62UTj96GKtj4wN+OwvrQsgP37fQ
+VUXThCML796qqFNLVDCAAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799-cert.pub
new file mode 100644
index 0000000000..80312fbe0d
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIC1z1LkrZhMz1mBWPU8sJIuH59v+ig4OK/B4/x8jLAtUAAAAID96GKtj4wN+OwvrQsgP37fQVUXThCML796qqFNLVDCAAAAAAAAAAx8AAAABAAAAC3Jldm9rZWQgNzk5AAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABASNkJSbdRDARfgbqPOnuES0o6m6VZ7RC2XLPm3uwTqCvMqtHbFvq9etMddSUIR4XXah6ef+O7CJDk/Yjpkn+2CA== ./tst-keys/revoked-0799.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799.pub
new file mode 100644
index 0000000000..1f0556cd05
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID96GKtj4wN+OwvrQsgP37fQVUXThCML796qqFNLVDCA
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999
new file mode 100644
index 0000000000..f05a1e41fc
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBIdwRXE815oNRYuS71olA1jkbB81YVApvHpKxCRuvugAAAAIgzBpObMwaT
+mwAAAAtzc2gtZWQyNTUxOQAAACBIdwRXE815oNRYuS71olA1jkbB81YVApvHpKxCRuvugA
+AAAECxY5wx3XKIhMT+ajMZXPl51x8rkCPBq6gUgZV3Uqpu7Eh3BFcTzXmg1Fi5LvWiUDWO
+RsHzVhUCm8ekrEJG6+6AAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999-cert.pub
new file mode 100644
index 0000000000..4aedb7770f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGt3nV/XJmtz9sQGP2fiZiKOH7mkPhezN3S+8TnsVcQjAAAAIEh3BFcTzXmg1Fi5LvWiUDWORsHzVhUCm8ekrEJG6+6AAAAAAAAAA+cAAAABAAAAC3Jldm9rZWQgOTk5AAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABAvLVCRCs7CV0JSXYL8ge4iRxL4y48bYuvu3YimKZDg7NdCXqw/jkaCsxJykRzb/xVnQDoNVCQQuzydt/I13FdBA== ./tst-keys/revoked-0999.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999.pub
new file mode 100644
index 0000000000..c837fe06ae
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEh3BFcTzXmg1Fi5LvWiUDWORsHzVhUCm8ekrEJG6+6A
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca
new file mode 100644
index 0000000000..47e01fb5c2
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAIgok4I2KJOC
+NgAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzg
+AAAEAEN+knz2qOyj+jbY+SJSHYQhlJoB1u9jLqoQoiAerI3hbReZevLKczhayKUADRdAvZ
+5DXVzAJpQkcB4MPdQu/OAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca.pub
new file mode 100644
index 0000000000..2b92f89e71
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/O
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2
new file mode 100644
index 0000000000..770ceee2e7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDsEBCX5jBwggkpt4XZXct1fOhDBuvgLL0KMpGoHRtj9wAAAIjMEwOtzBMD
+rQAAAAtzc2gtZWQyNTUxOQAAACDsEBCX5jBwggkpt4XZXct1fOhDBuvgLL0KMpGoHRtj9w
+AAAEAurE2/d7VhoEJeNFdDnVS7lpBRoMe/zAjA8dJRP1Z/I+wQEJfmMHCCCSm3hdldy3V8
+6EMG6+AsvQoykagdG2P3AAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2.pub
new file mode 100644
index 0000000000..a177fd0f24
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOwQEJfmMHCCCSm3hdldy3V86EMG6+AsvQoykagdG2P3
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-hash b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-hash
new file mode 100644
index 0000000000..c6f2361ffd
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-hash
@@ -0,0 +1,11 @@
+hash: SHA256:RvNFBEc/N9jsm3toDkitgr/wnWu/6qWBHo4Xmh5ZUpM
+hash: SHA256:qu2IwCnItWWX+orXv0rjCeT4i++2O6ViTzLye6kyWzU
+hash: SHA256:qQTACAkAJxYk1zvSQ+Rx9wa2IuOFJKtaEy/XwxM89J0
+hash: SHA256:Fe4GdmipzulS9oMB/h3U69tSm5wil6bTUKSJCT+Jf3E
+hash: SHA256:esUK/whZ5oJeRFNeOrHK1bbx9dKC+nRITZ7up7HJaGA
+hash: SHA256:xkii+r6t9rEBFYkx1b3dGNXzEs69M5NUMfHP05ypSdI
+hash: SHA256:lZrSycKcBNvUafU9y4R0EEbDaQWqMFvIGM9M+VKt2zk
+hash: SHA256:/2bgZOiYEH2UVahUllNaQ5P0advEB7liCPkp+aNVKDk
+hash: SHA256:He3c0W5o/P1I0pK5/VusqD5V6duAMeZl6f+6Yy5P1z0
+hash: SHA256:5V5Xw2lgcAGR8dO9cbgRmCNlhcCsBBv/hmEstKsqKr4
+hash: SHA256:T7s26JPzzRP2WHOcw3OjLwWo8ZZTkfo2jBCrRfJ6BR4
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-keyid b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-keyid
new file mode 100644
index 0000000000..592ddb4e78
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-keyid
@@ -0,0 +1,512 @@
+id: revoked 1
+id: revoked 2
+id: revoked 3
+id: revoked 4
+id: revoked 10
+id: revoked 15
+id: revoked 30
+id: revoked 50
+id: revoked 90
+id: revoked 300
+id: revoked 301
+id: revoked 302
+id: revoked 303
+id: revoked 304
+id: revoked 305
+id: revoked 306
+id: revoked 307
+id: revoked 308
+id: revoked 309
+id: revoked 310
+id: revoked 311
+id: revoked 312
+id: revoked 313
+id: revoked 314
+id: revoked 315
+id: revoked 316
+id: revoked 317
+id: revoked 318
+id: revoked 319
+id: revoked 320
+id: revoked 321
+id: revoked 322
+id: revoked 323
+id: revoked 324
+id: revoked 325
+id: revoked 326
+id: revoked 327
+id: revoked 328
+id: revoked 329
+id: revoked 330
+id: revoked 331
+id: revoked 332
+id: revoked 333
+id: revoked 334
+id: revoked 335
+id: revoked 336
+id: revoked 337
+id: revoked 338
+id: revoked 339
+id: revoked 340
+id: revoked 341
+id: revoked 342
+id: revoked 343
+id: revoked 344
+id: revoked 345
+id: revoked 346
+id: revoked 347
+id: revoked 348
+id: revoked 349
+id: revoked 350
+id: revoked 351
+id: revoked 352
+id: revoked 353
+id: revoked 354
+id: revoked 355
+id: revoked 356
+id: revoked 357
+id: revoked 358
+id: revoked 359
+id: revoked 360
+id: revoked 361
+id: revoked 362
+id: revoked 363
+id: revoked 364
+id: revoked 365
+id: revoked 366
+id: revoked 367
+id: revoked 368
+id: revoked 369
+id: revoked 370
+id: revoked 371
+id: revoked 372
+id: revoked 373
+id: revoked 374
+id: revoked 375
+id: revoked 376
+id: revoked 377
+id: revoked 378
+id: revoked 379
+id: revoked 380
+id: revoked 381
+id: revoked 382
+id: revoked 383
+id: revoked 384
+id: revoked 385
+id: revoked 386
+id: revoked 387
+id: revoked 388
+id: revoked 389
+id: revoked 390
+id: revoked 391
+id: revoked 392
+id: revoked 393
+id: revoked 394
+id: revoked 395
+id: revoked 396
+id: revoked 397
+id: revoked 398
+id: revoked 399
+id: revoked 400
+id: revoked 401
+id: revoked 402
+id: revoked 403
+id: revoked 404
+id: revoked 405
+id: revoked 406
+id: revoked 407
+id: revoked 408
+id: revoked 409
+id: revoked 410
+id: revoked 411
+id: revoked 412
+id: revoked 413
+id: revoked 414
+id: revoked 415
+id: revoked 416
+id: revoked 417
+id: revoked 418
+id: revoked 419
+id: revoked 420
+id: revoked 421
+id: revoked 422
+id: revoked 423
+id: revoked 424
+id: revoked 425
+id: revoked 426
+id: revoked 427
+id: revoked 428
+id: revoked 429
+id: revoked 430
+id: revoked 431
+id: revoked 432
+id: revoked 433
+id: revoked 434
+id: revoked 435
+id: revoked 436
+id: revoked 437
+id: revoked 438
+id: revoked 439
+id: revoked 440
+id: revoked 441
+id: revoked 442
+id: revoked 443
+id: revoked 444
+id: revoked 445
+id: revoked 446
+id: revoked 447
+id: revoked 448
+id: revoked 449
+id: revoked 450
+id: revoked 451
+id: revoked 452
+id: revoked 453
+id: revoked 454
+id: revoked 455
+id: revoked 456
+id: revoked 457
+id: revoked 458
+id: revoked 459
+id: revoked 460
+id: revoked 461
+id: revoked 462
+id: revoked 463
+id: revoked 464
+id: revoked 465
+id: revoked 466
+id: revoked 467
+id: revoked 468
+id: revoked 469
+id: revoked 470
+id: revoked 471
+id: revoked 472
+id: revoked 473
+id: revoked 474
+id: revoked 475
+id: revoked 476
+id: revoked 477
+id: revoked 478
+id: revoked 479
+id: revoked 480
+id: revoked 481
+id: revoked 482
+id: revoked 483
+id: revoked 484
+id: revoked 485
+id: revoked 486
+id: revoked 487
+id: revoked 488
+id: revoked 489
+id: revoked 490
+id: revoked 491
+id: revoked 492
+id: revoked 493
+id: revoked 494
+id: revoked 495
+id: revoked 496
+id: revoked 497
+id: revoked 498
+id: revoked 500
+id: revoked 501
+id: revoked 502
+id: revoked 503
+id: revoked 504
+id: revoked 505
+id: revoked 506
+id: revoked 507
+id: revoked 508
+id: revoked 509
+id: revoked 510
+id: revoked 511
+id: revoked 512
+id: revoked 513
+id: revoked 514
+id: revoked 515
+id: revoked 516
+id: revoked 517
+id: revoked 518
+id: revoked 519
+id: revoked 520
+id: revoked 521
+id: revoked 522
+id: revoked 523
+id: revoked 524
+id: revoked 525
+id: revoked 526
+id: revoked 527
+id: revoked 528
+id: revoked 529
+id: revoked 530
+id: revoked 531
+id: revoked 532
+id: revoked 533
+id: revoked 534
+id: revoked 535
+id: revoked 536
+id: revoked 537
+id: revoked 538
+id: revoked 539
+id: revoked 540
+id: revoked 541
+id: revoked 542
+id: revoked 543
+id: revoked 544
+id: revoked 545
+id: revoked 546
+id: revoked 547
+id: revoked 548
+id: revoked 549
+id: revoked 550
+id: revoked 551
+id: revoked 552
+id: revoked 553
+id: revoked 554
+id: revoked 555
+id: revoked 556
+id: revoked 557
+id: revoked 558
+id: revoked 559
+id: revoked 560
+id: revoked 561
+id: revoked 562
+id: revoked 563
+id: revoked 564
+id: revoked 565
+id: revoked 566
+id: revoked 567
+id: revoked 568
+id: revoked 569
+id: revoked 570
+id: revoked 571
+id: revoked 572
+id: revoked 573
+id: revoked 574
+id: revoked 575
+id: revoked 576
+id: revoked 577
+id: revoked 578
+id: revoked 579
+id: revoked 580
+id: revoked 581
+id: revoked 582
+id: revoked 583
+id: revoked 584
+id: revoked 585
+id: revoked 586
+id: revoked 587
+id: revoked 588
+id: revoked 589
+id: revoked 590
+id: revoked 591
+id: revoked 592
+id: revoked 593
+id: revoked 594
+id: revoked 595
+id: revoked 596
+id: revoked 597
+id: revoked 598
+id: revoked 599
+id: revoked 600
+id: revoked 601
+id: revoked 602
+id: revoked 603
+id: revoked 604
+id: revoked 605
+id: revoked 606
+id: revoked 607
+id: revoked 608
+id: revoked 609
+id: revoked 610
+id: revoked 611
+id: revoked 612
+id: revoked 613
+id: revoked 614
+id: revoked 615
+id: revoked 616
+id: revoked 617
+id: revoked 618
+id: revoked 619
+id: revoked 620
+id: revoked 621
+id: revoked 622
+id: revoked 623
+id: revoked 624
+id: revoked 625
+id: revoked 626
+id: revoked 627
+id: revoked 628
+id: revoked 629
+id: revoked 630
+id: revoked 631
+id: revoked 632
+id: revoked 633
+id: revoked 634
+id: revoked 635
+id: revoked 636
+id: revoked 637
+id: revoked 638
+id: revoked 639
+id: revoked 640
+id: revoked 641
+id: revoked 642
+id: revoked 643
+id: revoked 644
+id: revoked 645
+id: revoked 646
+id: revoked 647
+id: revoked 648
+id: revoked 649
+id: revoked 650
+id: revoked 651
+id: revoked 652
+id: revoked 653
+id: revoked 654
+id: revoked 655
+id: revoked 656
+id: revoked 657
+id: revoked 658
+id: revoked 659
+id: revoked 660
+id: revoked 661
+id: revoked 662
+id: revoked 663
+id: revoked 664
+id: revoked 665
+id: revoked 666
+id: revoked 667
+id: revoked 668
+id: revoked 669
+id: revoked 670
+id: revoked 671
+id: revoked 672
+id: revoked 673
+id: revoked 674
+id: revoked 675
+id: revoked 676
+id: revoked 677
+id: revoked 678
+id: revoked 679
+id: revoked 680
+id: revoked 681
+id: revoked 682
+id: revoked 683
+id: revoked 684
+id: revoked 685
+id: revoked 686
+id: revoked 687
+id: revoked 688
+id: revoked 689
+id: revoked 690
+id: revoked 691
+id: revoked 692
+id: revoked 693
+id: revoked 694
+id: revoked 695
+id: revoked 696
+id: revoked 697
+id: revoked 698
+id: revoked 699
+id: revoked 700
+id: revoked 701
+id: revoked 702
+id: revoked 703
+id: revoked 704
+id: revoked 705
+id: revoked 706
+id: revoked 707
+id: revoked 708
+id: revoked 709
+id: revoked 710
+id: revoked 711
+id: revoked 712
+id: revoked 713
+id: revoked 714
+id: revoked 715
+id: revoked 716
+id: revoked 717
+id: revoked 718
+id: revoked 719
+id: revoked 720
+id: revoked 721
+id: revoked 722
+id: revoked 723
+id: revoked 724
+id: revoked 725
+id: revoked 726
+id: revoked 727
+id: revoked 728
+id: revoked 729
+id: revoked 730
+id: revoked 731
+id: revoked 732
+id: revoked 733
+id: revoked 734
+id: revoked 735
+id: revoked 736
+id: revoked 737
+id: revoked 738
+id: revoked 739
+id: revoked 740
+id: revoked 741
+id: revoked 742
+id: revoked 743
+id: revoked 744
+id: revoked 745
+id: revoked 746
+id: revoked 747
+id: revoked 748
+id: revoked 749
+id: revoked 750
+id: revoked 751
+id: revoked 752
+id: revoked 753
+id: revoked 754
+id: revoked 755
+id: revoked 756
+id: revoked 757
+id: revoked 758
+id: revoked 759
+id: revoked 760
+id: revoked 761
+id: revoked 762
+id: revoked 763
+id: revoked 764
+id: revoked 765
+id: revoked 766
+id: revoked 767
+id: revoked 768
+id: revoked 769
+id: revoked 770
+id: revoked 771
+id: revoked 772
+id: revoked 773
+id: revoked 774
+id: revoked 775
+id: revoked 776
+id: revoked 777
+id: revoked 778
+id: revoked 779
+id: revoked 780
+id: revoked 781
+id: revoked 782
+id: revoked 783
+id: revoked 784
+id: revoked 785
+id: revoked 786
+id: revoked 787
+id: revoked 788
+id: revoked 789
+id: revoked 790
+id: revoked 791
+id: revoked 792
+id: revoked 793
+id: revoked 794
+id: revoked 795
+id: revoked 796
+id: revoked 797
+id: revoked 798
+id: revoked 799
+id: revoked 999
+id: revoked 1000
+id: revoked 1001
+id: revoked 1002
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-serials b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-serials
new file mode 100644
index 0000000000..b20fec292d
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-serials
@@ -0,0 +1,19 @@
+serial: 1-4
+serial: 10
+serial: 15
+serial: 30
+serial: 50
+serial: 90
+serial: 999
+# The following sum to 500-799
+serial: 500
+serial: 501
+serial: 502
+serial: 503-600
+serial: 700-797
+serial: 798
+serial: 799
+serial: 599-701
+# Some multiple consecutive serial number ranges
+serial: 10000-20000
+serial: 30000-40000
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha1 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha1
new file mode 100644
index 0000000000..475e90cb1c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha1
@@ -0,0 +1,11 @@
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC29QZ72HtyCdaNo6p2GH3fJpUynwkvs8Acwn66G7YTh
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0ZC4fqgbBgROeX1sOEPr4uMVNfPdJ62bVo/zvSMRQx
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG5IH9T7FY47VJDUoyOlB/iqCN4pO8dgOrxclmKN5R5w
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINjhG4+EnQy8YHLsfE8+IQwNWZVn1GBYX75pwxBCZGmy
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXm+qgTs+sO+9zvoZBxkQD39R2rQqQCVezxQoGjKui5
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL8+aQKja+SbqxfWR61FCcsbBw2jaF/KHvcqdP2Fbp6Q
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAOL7IULalT2Izo9TgRf1t2HNpZ5WCZJH5oRCd9LK3BN
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGVaSedhPkl2Hrx1nOOKT2E52ADsBebawws87NN1+P6e
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICxyaxj7pRVDeh/gxJem9BLhoUQKGnKXHfDrB/GtC1KB
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID96GKtj4wN+OwvrQsgP37fQVUXThCML796qqFNLVDCA
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEh3BFcTzXmg1Fi5LvWiUDWORsHzVhUCm8ekrEJG6+6A
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha256 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha256
new file mode 100644
index 0000000000..13109e9049
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha256
@@ -0,0 +1,11 @@
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC29QZ72HtyCdaNo6p2GH3fJpUynwkvs8Acwn66G7YTh
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0ZC4fqgbBgROeX1sOEPr4uMVNfPdJ62bVo/zvSMRQx
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG5IH9T7FY47VJDUoyOlB/iqCN4pO8dgOrxclmKN5R5w
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINjhG4+EnQy8YHLsfE8+IQwNWZVn1GBYX75pwxBCZGmy
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXm+qgTs+sO+9zvoZBxkQD39R2rQqQCVezxQoGjKui5
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL8+aQKja+SbqxfWR61FCcsbBw2jaF/KHvcqdP2Fbp6Q
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAOL7IULalT2Izo9TgRf1t2HNpZ5WCZJH5oRCd9LK3BN
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGVaSedhPkl2Hrx1nOOKT2E52ADsBebawws87NN1+P6e
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICxyaxj7pRVDeh/gxJem9BLhoUQKGnKXHfDrB/GtC1KB
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID96GKtj4wN+OwvrQsgP37fQVUXThCML796qqFNLVDCA
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEh3BFcTzXmg1Fi5LvWiUDWORsHzVhUCm8ekrEJG6+6A
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005
new file mode 100644
index 0000000000..d82a0b5d3c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDqONQediveIXoseoT+MWp9yEdMO7hP7F4fAno6gunyoAAAAIig1MZroNTG
+awAAAAtzc2gtZWQyNTUxOQAAACDqONQediveIXoseoT+MWp9yEdMO7hP7F4fAno6gunyoA
+AAAEBSEPLoX4NVkAchYZEGi7hjd5NoVBWuoxqluCGt/fWrYeo41B52K94heix6hP4xan3I
+R0w7uE/sXh8CejqC6fKgAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005-cert.pub
new file mode 100644
index 0000000000..59ea422c2a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGnzDhP/hp83ipkW8T7f0CIXJuPK7ldbJFKDUrkvn6J1AAAAIOo41B52K94heix6hP4xan3IR0w7uE/sXh8CejqC6fKgAAAAAAAAAAUAAAABAAAACXJldm9rZWQgNQAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgFtF5l68spzOFrIpQANF0C9nkNdXMAmlCRwHgw91C784AAABTAAAAC3NzaC1lZDI1NTE5AAAAQO9W58IrK+I0o2us9Hs/QBkrEe1YIgl6PzCMsu/Zu/tdZxGDK5Pxoz7tKzXezS9LPGQfZ3fVdl58PZC1DtxQ5gU= ./tst-keys/unrevoked-0005.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005.pub
new file mode 100644
index 0000000000..081ac6c63a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOo41B52K94heix6hP4xan3IR0w7uE/sXh8CejqC6fKg
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009
new file mode 100644
index 0000000000..947949847c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDXQqTeALQCMo64B4EX5abjRvrjVu69Mnxgg2q0SB5/oQAAAIgIqeXLCKnl
+ywAAAAtzc2gtZWQyNTUxOQAAACDXQqTeALQCMo64B4EX5abjRvrjVu69Mnxgg2q0SB5/oQ
+AAAECubGChJGu90ZNiP/zF+tTtr0+l7y8BrTDMQ0m0+cU0qtdCpN4AtAIyjrgHgRflpuNG
++uNW7r0yfGCDarRIHn+hAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009-cert.pub
new file mode 100644
index 0000000000..9ee889075b
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIERRY0M1bHm2Qjyo105OCHWp0UCRHLP0xkMuHnkMDP5eAAAAINdCpN4AtAIyjrgHgRflpuNG+uNW7r0yfGCDarRIHn+hAAAAAAAAAAkAAAABAAAACXJldm9rZWQgOQAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgFtF5l68spzOFrIpQANF0C9nkNdXMAmlCRwHgw91C784AAABTAAAAC3NzaC1lZDI1NTE5AAAAQFsA4xJHRCXSyq6GHkKdemfbg+jvUZxHlu/UBoZf4esEHAtx0mXiajbUwkWzkh1vCtxZNZhiLIhxqDcNMu+O+wo= ./tst-keys/unrevoked-0009.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009.pub
new file mode 100644
index 0000000000..74a797b960
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINdCpN4AtAIyjrgHgRflpuNG+uNW7r0yfGCDarRIHn+h
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014
new file mode 100644
index 0000000000..6fa4fd93bd
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDvTTMHyjozzZabuUzy61XOKBm4klUjUGSWYtX6T4XtEwAAAIhyFdxYchXc
+WAAAAAtzc2gtZWQyNTUxOQAAACDvTTMHyjozzZabuUzy61XOKBm4klUjUGSWYtX6T4XtEw
+AAAEBtC+f4bz1/qtq5K2Rf+0bPeY3P0OWdD3rvrlGPh8wN5u9NMwfKOjPNlpu5TPLrVc4o
+GbiSVSNQZJZi1fpPhe0TAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014-cert.pub
new file mode 100644
index 0000000000..bb954f9337
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIPes2n/Xk4mm4OpuvHDqx9+76vm+SmFgc9d7ATGT1+C8AAAAIO9NMwfKOjPNlpu5TPLrVc4oGbiSVSNQZJZi1fpPhe0TAAAAAAAAAA4AAAABAAAACnJldm9rZWQgMTQAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDGVORypw3DoMuWBu0V4cH/OgRBstD5cY37CfLrVZpmGv9jDRXVNQee7vYowk0r3XvQPoUecQBIMZGAQtEiw18E ./tst-keys/unrevoked-0014.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014.pub
new file mode 100644
index 0000000000..4a866e41b8
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO9NMwfKOjPNlpu5TPLrVc4oGbiSVSNQZJZi1fpPhe0T
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016
new file mode 100644
index 0000000000..62d5027eff
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBWKMDlSwSGo4dcBAZmL+Xxk64Wp/ZfFSu2vkp82JXQCQAAAIjUcNt51HDb
+eQAAAAtzc2gtZWQyNTUxOQAAACBWKMDlSwSGo4dcBAZmL+Xxk64Wp/ZfFSu2vkp82JXQCQ
+AAAEC1V7PD5tJSOUZtpfqVfWyiSIMJkCDFZzTmFs7GBpJE71YowOVLBIajh1wEBmYv5fGT
+rhan9l8VK7a+SnzYldAJAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016-cert.pub
new file mode 100644
index 0000000000..367e4ab70a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAICGqa0xwr0etbKquuBy5/hYQ/rbMrKfEE6XShgb4YWpUAAAAIFYowOVLBIajh1wEBmYv5fGTrhan9l8VK7a+SnzYldAJAAAAAAAAABAAAAABAAAACnJldm9rZWQgMTYAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEBKVetE3dsch2wjMIHGoiH8zp6gFMn1KgGKn01EPc1A08a/JKNvaSDYhlARLjiBzjIUGlykhHTTr4EcHTPWl58P ./tst-keys/unrevoked-0016.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016.pub
new file mode 100644
index 0000000000..47cac1e76c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFYowOVLBIajh1wEBmYv5fGTrhan9l8VK7a+SnzYldAJ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029
new file mode 100644
index 0000000000..589daa6afe
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACA3B1NQ9RFEkJUGcIUcCL22yMVEeob8/PUsk9lYH43vPwAAAIjxPrzV8T68
+1QAAAAtzc2gtZWQyNTUxOQAAACA3B1NQ9RFEkJUGcIUcCL22yMVEeob8/PUsk9lYH43vPw
+AAAED89ht9KdlYRfsKwh+pzh6BOvPf/U58QBkw1d3LfKnn+jcHU1D1EUSQlQZwhRwIvbbI
+xUR6hvz89SyT2Vgfje8/AAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029-cert.pub
new file mode 100644
index 0000000000..1bf3883ce0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIEVLRuchC4z7/EqITmyqCxOyhC7/enmFWsalP8FFFYiXAAAAIDcHU1D1EUSQlQZwhRwIvbbIxUR6hvz89SyT2Vgfje8/AAAAAAAAAB0AAAABAAAACnJldm9rZWQgMjkAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEChRFz/Zb6b3znoIWJjd8OTmCIEH7YE/fKWtyWHoGjz02G4VnCfwuHp23yD+k1XsoOGC7xcSnQeqZ19160HDNgC ./tst-keys/unrevoked-0029.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029.pub
new file mode 100644
index 0000000000..4072d920bd
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDcHU1D1EUSQlQZwhRwIvbbIxUR6hvz89SyT2Vgfje8/
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049
new file mode 100644
index 0000000000..b5788a0b5e
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACD2mB5GBuavtb/bX7W54OmUCCJzUWBwG7cQ4q/jon1MBQAAAIjRkEU40ZBF
+OAAAAAtzc2gtZWQyNTUxOQAAACD2mB5GBuavtb/bX7W54OmUCCJzUWBwG7cQ4q/jon1MBQ
+AAAECuUtJb+T0um2mGvjD/ZZpbtjIhWc3jGVbzuDnEovOjnPaYHkYG5q+1v9tftbng6ZQI
+InNRYHAbtxDir+OifUwFAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049-cert.pub
new file mode 100644
index 0000000000..587cf6220b
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAILZPLEL5xQ8HDLa8pJhchJ3EEhZcjMqACCAEeL+U6c/QAAAAIPaYHkYG5q+1v9tftbng6ZQIInNRYHAbtxDir+OifUwFAAAAAAAAADEAAAABAAAACnJldm9rZWQgNDkAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEB2GglzoC1VgsYNAVd5BDsLbeR5M5hHcVVvNsGnK1QCXMj56cgfkbXLj6W6tjJEEFY4G+KPJh1F/SGJi02P5lkJ ./tst-keys/unrevoked-0049.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049.pub
new file mode 100644
index 0000000000..07d5369090
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPaYHkYG5q+1v9tftbng6ZQIInNRYHAbtxDir+OifUwF
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051
new file mode 100644
index 0000000000..52d3283a79
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACD39ygfAlHPhZWU8inWu1hypIlQTChQxSKKB6iaV6Q0lQAAAIgMawsqDGsL
+KgAAAAtzc2gtZWQyNTUxOQAAACD39ygfAlHPhZWU8inWu1hypIlQTChQxSKKB6iaV6Q0lQ
+AAAEB4Ng9MekhsMKYDaBcOUWdxmi1rjgCsPOOfpABTxiCef/f3KB8CUc+FlZTyKda7WHKk
+iVBMKFDFIooHqJpXpDSVAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051-cert.pub
new file mode 100644
index 0000000000..5b4bd11247
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGTNYRrlJ1vExK7dume319Krn4YW6wyZc4PzZLjZoB8zAAAAIPf3KB8CUc+FlZTyKda7WHKkiVBMKFDFIooHqJpXpDSVAAAAAAAAADMAAAABAAAACnJldm9rZWQgNTEAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEAgUiwWKerMo8nuejTER/EmM6ZUpmXjgFwPCpb1LAxBJH71iOnyF9S0gp+CSmjqiTS2yuQajSMen64wOdJCX7wF ./tst-keys/unrevoked-0051.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051.pub
new file mode 100644
index 0000000000..88867e58da
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPf3KB8CUc+FlZTyKda7WHKkiVBMKFDFIooHqJpXpDSV
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499
new file mode 100644
index 0000000000..8f59be9e5d
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACCpwI1aCbAOVvA7NJhLtBNpR4tiGGtTQ019wjKL6zJ/uQAAAIhllrzrZZa8
+6wAAAAtzc2gtZWQyNTUxOQAAACCpwI1aCbAOVvA7NJhLtBNpR4tiGGtTQ019wjKL6zJ/uQ
+AAAECQ6o+3J9W3wXFWEcrPJl5qJZudUPmPdKF7SYxcMTrVP6nAjVoJsA5W8Ds0mEu0E2lH
+i2IYa1NDTX3CMovrMn+5AAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499-cert.pub
new file mode 100644
index 0000000000..a6e76f12f7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIJvt1IxZsGIIS9DDCCKiD13Dbs5Af5ouews+YwZ9FoydAAAAIKnAjVoJsA5W8Ds0mEu0E2lHi2IYa1NDTX3CMovrMn+5AAAAAAAAAfMAAAABAAAAC3Jldm9rZWQgNDk5AAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABAMaA4UjND4LX9kdHjhgWJjGzzs/xUBwxQQcAmNgwmmQzmkwj8ctWBBA1+TkBMcZbSNUWBdclT4UcnDPEYqG1NBg== ./tst-keys/unrevoked-0499.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499.pub
new file mode 100644
index 0000000000..5a3acbb245
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKnAjVoJsA5W8Ds0mEu0E2lHi2IYa1NDTX3CMovrMn+5
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800
new file mode 100644
index 0000000000..9684d727f5
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAn5h8A2vYJ1+IWVtdLMulUQKCqlVLHpcHEFqYC5gtGlwAAAIh2lf7UdpX+
+1AAAAAtzc2gtZWQyNTUxOQAAACAn5h8A2vYJ1+IWVtdLMulUQKCqlVLHpcHEFqYC5gtGlw
+AAAEAEXGgMPKs3HwkQmNdVkbO3PcaBVCBEv1l8yy/ly30jPSfmHwDa9gnX4hZW10sy6VRA
+oKqVUselwcQWpgLmC0aXAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800-cert.pub
new file mode 100644
index 0000000000..ab47a2bcf4
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIPAKFTJ25v9CsCppsQ/FwXAZgntAIdQHUXo0KQ3FrlTzAAAAICfmHwDa9gnX4hZW10sy6VRAoKqVUselwcQWpgLmC0aXAAAAAAAAAyAAAAABAAAAC3Jldm9rZWQgODAwAAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABA16aKfsgD0iZ+qc2b1AxBHZ/nyczN2Xjbhg4eJm/6cPSkBHs8uan5e8yPBIQJq2LztC3If6Z6PARoWUnIKb43CQ== ./tst-keys/unrevoked-0800.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800.pub
new file mode 100644
index 0000000000..3a41f29a84
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICfmHwDa9gnX4hZW10sy6VRAoKqVUselwcQWpgLmC0aX
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010
new file mode 100644
index 0000000000..89df71745a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAg0jawQzRMO/ESfFm6yDc66J5kjasOqTb7rmQSU6Nk3QAAAIhczXMoXM1z
+KAAAAAtzc2gtZWQyNTUxOQAAACAg0jawQzRMO/ESfFm6yDc66J5kjasOqTb7rmQSU6Nk3Q
+AAAEAdeQiqpyZqBaffmgy+UrvFVpygD0n8isn3zjumVNtKxiDSNrBDNEw78RJ8WbrINzro
+nmSNqw6pNvuuZBJTo2TdAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010-cert.pub
new file mode 100644
index 0000000000..2d0fe53663
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIITg9nSjjofIKXTKf2byvYL3Ce43PP9Dtrbj/+AlfgEtAAAAICDSNrBDNEw78RJ8WbrINzronmSNqw6pNvuuZBJTo2TdAAAAAAAAA/IAAAABAAAADHJldm9rZWQgMTAxMAAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgFtF5l68spzOFrIpQANF0C9nkNdXMAmlCRwHgw91C784AAABTAAAAC3NzaC1lZDI1NTE5AAAAQIndHhKILtU0+FkKKw1KmhaHQS3p1KiQdld/2P5jpcEgb292iY+ICU+aHXKvS8qGM2aMImv8835NEyWy/MB74QM= ./tst-keys/unrevoked-1010.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010.pub
new file mode 100644
index 0000000000..05c5eac539
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICDSNrBDNEw78RJ8WbrINzronmSNqw6pNvuuZBJTo2Td
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011
new file mode 100644
index 0000000000..38b823270e
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACCd4IBQx9BhO9FzYMOKu3cKgBcwUwb7XzS3uI26RgmEYgAAAIjHvhtux74b
+bgAAAAtzc2gtZWQyNTUxOQAAACCd4IBQx9BhO9FzYMOKu3cKgBcwUwb7XzS3uI26RgmEYg
+AAAEBsteyDUYUNwgY3SMkMs0guy8MJfek2kuvH35zEpVf6Hp3ggFDH0GE70XNgw4q7dwqA
+FzBTBvtfNLe4jbpGCYRiAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011-cert.pub
new file mode 100644
index 0000000000..46716383ce
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIMjD2+xjmUC1VviOH+peT9C81Y4xjyTue/F69nFKmQBMAAAAIJ3ggFDH0GE70XNgw4q7dwqAFzBTBvtfNLe4jbpGCYRiAAAAAAAAA/MAAAABAAAADHJldm9rZWQgMTAxMQAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgFtF5l68spzOFrIpQANF0C9nkNdXMAmlCRwHgw91C784AAABTAAAAC3NzaC1lZDI1NTE5AAAAQNENdVFCE02X6z+wFJtm2DQcgdc4oov9DyFKLPqLrogo+pVao5QwOkeJ2J/tmp40H2+uP/jrDlQuCvOcoQGHqwY= ./tst-keys/unrevoked-1011.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011.pub
new file mode 100644
index 0000000000..080907734c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ3ggFDH0GE70XNgw4q7dwqAFzBTBvtfNLe4jbpGCYRi
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key
new file mode 100644
index 0000000000..ee3f922c2f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACA3ivU7wf37jE1ITC5KQjVeVlyFTkgWJxub8t380ovjiwAAAJDdMhQO3TIU
+DgAAAAtzc2gtZWQyNTUxOQAAACA3ivU7wf37jE1ITC5KQjVeVlyFTkgWJxub8t380ovjiw
+AAAEA4NlTFs3h2zqt5pSZ5S3dJb42GE7EjG16coKj70eELNDeK9TvB/fuMTUhMLkpCNV5W
+XIVOSBYnG5vy3fzSi+OLAAAADVRIV09AU0VBR044MDA=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key-cert.pub
new file mode 100644
index 0000000000..2be08be740
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGXo4+L/NyBl1VQDP39PxJP3LSzaqopqZGVP3cG0WoFAAAAAIDeK9TvB/fuMTUhMLkpCNV5WXIVOSBYnG5vy3fzSi+OLAAAAAAAAAAUAAAABAAAABnRlc3RlcgAAABYAAAASdGVzdGVyQGV4YW1wbGUuY29tAAAAAGbTroAAAAAAZyLIgAAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIMdEl+iOTEbf1RC3uicECtid+SaIMsAw7wrlWhOQTyBVAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEA/HwKB8J/kvkEsdxDou+UebnR9u30xPH6FEnbHLlfKbKMIXwLFIHnf9F6bTL36WhFDEDcSBGS19VBWBDRosM8L
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key.pub
new file mode 100644
index 0000000000..0255005400
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDeK9TvB/fuMTUhMLkpCNV5WXIVOSBYnG5vy3fzSi+OL
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/repo.bundle b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/repo.bundle
new file mode 100644
index 0000000000..c402f549cf
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/repo.bundle
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key
new file mode 100644
index 0000000000..3dd37be6b2
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBgEzmfD3DinWPe/H8yLLZ2dPhbnnyFiqe8EWcp0C3czgAAAJDhSMqA4UjK
+gAAAAAtzc2gtZWQyNTUxOQAAACBgEzmfD3DinWPe/H8yLLZ2dPhbnnyFiqe8EWcp0C3czg
+AAAEB1yC00NMYEAVzhDj9odGVL0EonaIkf5jdUZ/czJ0+SPWATOZ8PcOKdY978fzIstnZ0
++FuefIWKp7wRZynQLdzOAAAADVRIV09AU0VBR044MDA=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key-cert.pub
new file mode 100644
index 0000000000..de191d1870
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIFEmoWkYraMju0JI0b/0RQtR6RYo/OVp53EVf48L/Pu/AAAAIBmHlkHFlA7HkoTZcau80PH5zduQu41m8BqnH/1v2BwVAAAAAAAAAAEAAAABAAAACGFfa2V5X2lkAAAAFgAAABJ0ZXN0ZXJAZXhhbXBsZS5jb20AAAAAZtOugAAAAABm1QAAAAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAg2ifM9NMuXwQf7H/H5LCMhMjVqugyyN+jmcMoJUL2YLAAAABTAAAAC3NzaC1lZDI1NTE5AAAAQG1kXUido46YOnmwvkJuIAKyp6Q9Gr+lbdOQvU0St/Hc9HTTIxgDGyLpv0alIJpHOuSYUUUxDufvGKtLJK1duwg= ./signing_key.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key.pub
new file mode 100644
index 0000000000..e1210e72c0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AbstractSshSignatureTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AbstractSshSignatureTest.java
new file mode 100644
index 0000000000..fdfffce810
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AbstractSshSignatureTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.time.Instant;
+import java.time.ZoneOffset;
+
+import org.eclipse.jgit.api.CommitCommand;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Common setup for SSH signature tests.
+ */
+public abstract class AbstractSshSignatureTest extends RepositoryTestCase {
+
+ @Rule
+ public TemporaryFolder keys = new TemporaryFolder();
+
+ protected File certs;
+
+ protected Instant commitTime;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ copyResource("allowed_signers", keys.getRoot());
+ copyResource("other_key", keys.getRoot());
+ copyResource("other_key.pub", keys.getRoot());
+ copyResource("other_key-cert.pub", keys.getRoot());
+ copyResource("signing_key", keys.getRoot());
+ copyResource("signing_key.pub", keys.getRoot());
+ certs = keys.newFolder("certs");
+ copyResource("certs/expired.cert", certs);
+ copyResource("certs/no_principals.cert", certs);
+ copyResource("certs/other.cert", certs);
+ copyResource("certs/other-ca.cert", certs);
+ copyResource("certs/tester.cert", certs);
+ copyResource("certs/two_principals.cert", certs);
+ Repository repo = db;
+ StoredConfig config = repo.getConfig();
+ config.setString("gpg", null, "format", "ssh");
+ config.setString("gpg", "ssh", "allowedSignersFile",
+ keys.getRoot().toPath().resolve("allowed_signers").toString()
+ .replace('\\', '/'));
+ config.save();
+ // Run all tests with commit times on 2024-10-02T12:00:00Z. The test
+ // certificates are valid from 2024-09-01 to 2024-10-31, except the
+ // "expired" certificate which is valid only on 2024-09-01.
+ commitTime = Instant.parse("2024-10-02T12:00:00.00Z");
+ }
+
+ private void copyResource(String name, File directory) throws IOException {
+ try (InputStream in = this.getClass().getResourceAsStream(name)) {
+ int i = name.lastIndexOf('/');
+ String fileName = i < 0 ? name : name.substring(i + 1);
+ Files.copy(in, directory.toPath().resolve(fileName));
+ }
+ }
+
+ protected RevCommit createSignedCommit(String certificate,
+ String signingKey) throws Exception {
+ Repository repo = db;
+ Path key = keys.getRoot().toPath().resolve(signingKey);
+ if (certificate != null) {
+ Files.copy(certs.toPath().resolve(certificate),
+ keys.getRoot().toPath().resolve(signingKey),
+ StandardCopyOption.REPLACE_EXISTING);
+ }
+ PersonIdent commitAuthor = new PersonIdent("tester",
+ "tester@example.com", commitTime, ZoneOffset.UTC);
+ try (Git git = Git.wrap(repo)) {
+ writeTrashFile("foo.txt", "foo");
+ git.add().addFilepattern("foo.txt").call();
+ CommitCommand commit = git.commit();
+ commit.setAuthor(commitAuthor);
+ commit.setCommitter(commitAuthor);
+ commit.setMessage("Message");
+ commit.setSign(Boolean.TRUE);
+ commit.setSigningKey(key.toAbsolutePath().toString());
+ return commit.call();
+ }
+ }
+
+ protected RevCommit checkSshSignature(RevCommit c) {
+ byte[] sig = c.getRawGpgSignature();
+ assertNotNull(sig);
+ String signature = new String(sig, StandardCharsets.US_ASCII);
+ assertTrue("Not an SSH signature:\n" + signature,
+ signature.startsWith(Constants.SSH_SIGNATURE_PREFIX));
+ return c;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AllowedSignersParseTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AllowedSignersParseTest.java
new file mode 100644
index 0000000000..84d8179a3d
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AllowedSignersParseTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+
+import java.io.StreamCorruptedException;
+import java.time.Instant;
+
+import org.eclipse.jgit.junit.MockSystemReader;
+import org.eclipse.jgit.util.SystemReader;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the line parsing in {@link AllowedSigners}.
+ */
+public class AllowedSignersParseTest {
+
+ @Before
+ public void setup() {
+ // Uses GMT-03:30 as time zone.
+ SystemReader.setInstance(new MockSystemReader());
+ }
+
+ @After
+ public void tearDown() {
+ SystemReader.setInstance(null);
+ }
+
+ @Test
+ public void testValidDate() {
+ assertEquals(Instant.parse("2024-09-01T00:00:00.00Z"),
+ AllowedSigners.parseDate("20240901Z"));
+ assertEquals(Instant.parse("2024-09-01T01:02:00.00Z"),
+ AllowedSigners.parseDate("202409010102Z"));
+ assertEquals(Instant.parse("2024-09-01T01:02:03.00Z"),
+ AllowedSigners.parseDate("20240901010203Z"));
+ assertEquals(Instant.parse("2024-09-01T03:30:00.00Z"),
+ AllowedSigners.parseDate("20240901"));
+ assertEquals(Instant.parse("2024-09-01T04:32:00.00Z"),
+ AllowedSigners.parseDate("202409010102"));
+ assertEquals(Instant.parse("2024-09-01T04:32:03.00Z"),
+ AllowedSigners.parseDate("20240901010203"));
+ }
+
+ @Test
+ public void testInvalidDate() {
+ assertThrows(Exception.class, () -> AllowedSigners.parseDate("1234"));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parseDate("09/01/2024"));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parseDate("2024-09-01"));
+ }
+
+ private void checkValidKey(String expected, String input, int from)
+ throws StreamCorruptedException {
+ assertEquals(expected, AllowedSigners.parsePublicKey(input, from));
+ }
+ @Test
+ public void testValidPublicKey() throws StreamCorruptedException {
+ checkValidKey(
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO",
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO",
+ 0);
+ checkValidKey(
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO",
+ "xyzssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO",
+ 3);
+ checkValidKey(
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO",
+ "xyz ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO abc",
+ 3);
+ checkValidKey(
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO",
+ "xyz\tssh-ed25519 \tAAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO abc",
+ 3);
+ }
+
+ @Test
+ public void testInvalidPublicKey() {
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey(null, 0));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey("", 0));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey("foo", 0));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey("ssh-ed25519 bar", -1));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey("ssh-ed25519 bar", 12));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey("ssh-ed25519 bar", 13));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey("ssh-ed25519 bar", 16));
+ }
+
+ @Test
+ public void testValidDequote() {
+ assertEquals(new AllowedSigners.Dequoted("a\\bc", 4),
+ AllowedSigners.dequote("a\\bc", 0));
+ assertEquals(new AllowedSigners.Dequoted("a\\bc\"", 5),
+ AllowedSigners.dequote("a\\bc\"", 0));
+ assertEquals(new AllowedSigners.Dequoted("a\\b\"c", 5),
+ AllowedSigners.dequote("a\\b\"c", 0));
+ assertEquals(new AllowedSigners.Dequoted("a\\b\"c", 8),
+ AllowedSigners.dequote("\"a\\b\\\"c\"", 0));
+ assertEquals(new AllowedSigners.Dequoted("a\\b\"c", 11),
+ AllowedSigners.dequote("xyz\"a\\b\\\"c\"", 3));
+ assertEquals(new AllowedSigners.Dequoted("abc", 6),
+ AllowedSigners.dequote(" abc def", 3));
+ }
+
+ @Test
+ public void testInvalidDequote() {
+ assertThrows(Exception.class, () -> AllowedSigners.dequote("\"abc", 0));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.dequote("\"abc\\\"", 0));
+ }
+
+ @Test
+ public void testValidLine() throws Exception {
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "*@a.com" },
+ true, null, null, null,
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"),
+ AllowedSigners.parseLine(
+ "*@a.com cert-authority ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "*@a.com", "*@b.a.com" },
+ true, null, null, null,
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"),
+ AllowedSigners.parseLine(
+ "*@a.com,*@b.a.com cert-authority ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "foo@a.com" },
+ false, null, null, null,
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"),
+ AllowedSigners.parseLine(
+ "foo@a.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "foo@a.com" },
+ false, new String[] { "foo", "bar" }, null, null,
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"),
+ AllowedSigners.parseLine(
+ "foo@a.com namespaces=\"foo,bar\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "foo@a.com" },
+ false, null, Instant.parse("2024-09-01T03:30:00.00Z"), null,
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"),
+ AllowedSigners.parseLine(
+ "foo@a.com valid-After=\"20240901\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "*@a.com", "*@b.a.com" },
+ true, new String[] { "git" },
+ Instant.parse("2024-09-01T03:30:00.00Z"),
+ Instant.parse("2024-09-01T12:00:00.00Z"),
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"),
+ AllowedSigners.parseLine(
+ "*@a.com,*@b.a.com cert-authority namespaces=\"git\" valid-after=\"20240901\" valid-before=\"202409011200Z\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "foo@a.com" },
+ false, new String[] { "git" },
+ Instant.parse("2024-09-01T03:30:00.00Z"),
+ Instant.parse("2024-09-01T12:00:00.00Z"),
+ "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGxkz2AUld8eitmyIYlVV+Sot4jT3CigyBmvFRff0q4cSsKLx4x2TxGQeKKVueJEawtsUC2GNRV9FxXsTCUGcZU="),
+ AllowedSigners.parseLine(
+ "foo@a.com namespaces=\"git\" valid-after=\"20240901\" valid-before=\"202409011200Z\" ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGxkz2AUld8eitmyIYlVV+Sot4jT3CigyBmvFRff0q4cSsKLx4x2TxGQeKKVueJEawtsUC2GNRV9FxXsTCUGcZU="));
+ }
+
+ @Test
+ public void testInvalidLine() {
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "cert-authority ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "namespaces=\"git\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "valid-after=\"20240901\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "valid-before=\"20240901\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO foo@bar.com"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "a@a.com namespaces=\"\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "a@a.com namespaces=\",,,\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "a@a.com,,b@a.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ }
+
+ @Test
+ public void testSkippedLine() throws Exception {
+ assertNull(AllowedSigners.parseLine(null));
+ assertNull(AllowedSigners.parseLine(""));
+ assertNull(AllowedSigners.parseLine("# Comment"));
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrlLoadTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrlLoadTest.java
new file mode 100644
index 0000000000..9f9c3ca303
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrlLoadTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+
+import org.junit.Test;
+
+/**
+ * Tests loading an {@link OpenSshBinaryKrl}.
+ */
+public class OpenSshBinaryKrlLoadTest {
+
+ @Test
+ public void testLoad() throws Exception {
+ try (InputStream in = new BufferedInputStream(
+ this.getClass().getResourceAsStream("krl/krl"))) {
+ OpenSshBinaryKrl krl = OpenSshBinaryKrl.load(in, false);
+ assertNotNull(krl);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshKrlTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshKrlTest.java
new file mode 100644
index 0000000000..2fd7756d10
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshKrlTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.AfterClass;
+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;
+
+/**
+ * Tests for {@link OpenSshKrl} using binary KRLs.
+ */
+@RunWith(Parameterized.class)
+public class OpenSshKrlTest {
+
+ // The test data was generated using the public domain OpenSSH test script
+ // with some minor modifications (une ed25519 always, generate a "includes
+ // everything KRl, name the unrekoked keys "unrevoked*", generate a plain
+ // text KRL, and don't run the ssh-keygen tests). The original script is
+ // available at
+ // https://github.com/openssh/openssh-portable/blob/67a115e/regress/krl.sh
+
+ private static final String[] KRLS = {
+ "krl-empty", "krl-keys", "krl-all",
+ "krl-sha1", "krl-sha256", "krl-hash",
+ "krl-serial", "krl-keyid", "krl-cert", "krl-ca",
+ "krl-serial-wild", "krl-keyid-wild", "krl-text" };
+
+ private static final int[] REVOKED = { 1, 4, 10, 50, 90, 500, 510, 520, 550,
+ 799, 999 };
+
+ private static final int[] UNREVOKED = { 5, 9, 14, 16, 29, 49, 51, 499, 800,
+ 1010, 1011 };
+
+ private static class TestData {
+
+ String key;
+
+ String krl;
+
+ Boolean expected;
+
+ TestData(String key, String krl, boolean expected) {
+ this.key = key;
+ this.krl = krl;
+ this.expected = Boolean.valueOf(expected);
+ }
+
+ @Override
+ public String toString() {
+ return key + '-' + krl;
+ }
+ }
+
+ @Parameters(name = "{0}")
+ public static List<TestData> initTestData() {
+ List<TestData> tests = new ArrayList<>();
+ for (int i = 0; i < REVOKED.length; i++) {
+ String key = String.format("revoked-%04d",
+ Integer.valueOf(REVOKED[i]));
+ for (String krl : KRLS) {
+ boolean expected = !krl.endsWith("-empty");
+ tests.add(new TestData(key + "-cert.pub", krl, expected));
+ expected = krl.endsWith("-keys") || krl.endsWith("-all")
+ || krl.endsWith("-hash") || krl.endsWith("-sha1")
+ || krl.endsWith("-sha256") || krl.endsWith("-text");
+ tests.add(new TestData(key + ".pub", krl, expected));
+ }
+ }
+ for (int i = 0; i < UNREVOKED.length; i++) {
+ String key = String.format("unrevoked-%04d",
+ Integer.valueOf(UNREVOKED[i]));
+ for (String krl : KRLS) {
+ boolean expected = false;
+ tests.add(new TestData(key + ".pub", krl, expected));
+ expected = krl.endsWith("-ca");
+ tests.add(new TestData(key + "-cert.pub", krl, expected));
+ }
+ }
+ return tests;
+ }
+
+ private static Path tmp;
+
+ @BeforeClass
+ public static void setUp() throws IOException {
+ tmp = Files.createTempDirectory("krls");
+ for (String krl : KRLS) {
+ copyResource("krl/" + krl, tmp);
+ }
+ }
+
+ private static void copyResource(String name, Path directory)
+ throws IOException {
+ try (InputStream in = OpenSshKrlTest.class
+ .getResourceAsStream(name)) {
+ int i = name.lastIndexOf('/');
+ String fileName = i < 0 ? name : name.substring(i + 1);
+ Files.copy(in, directory.resolve(fileName));
+ }
+ }
+
+ @AfterClass
+ public static void cleanUp() throws Exception {
+ FileUtils.delete(tmp.toFile(), FileUtils.RECURSIVE);
+ }
+
+ // Injected by JUnit
+ @Parameter
+ public TestData data;
+
+ @Test
+ public void testIsRevoked() throws Exception {
+ OpenSshKrl krl = new OpenSshKrl(tmp.resolve(data.krl));
+ try (InputStream in = this.getClass()
+ .getResourceAsStream("krl/" + data.key)) {
+ PublicKey key = AuthorizedKeyEntry.readAuthorizedKeys(in, true)
+ .get(0)
+ .resolvePublicKey(null, PublicKeyEntryResolver.FAILING);
+ assertEquals(data.expected, Boolean.valueOf(krl.isRevoked(key)));
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SerialRangeSetTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SerialRangeSetTest.java
new file mode 100644
index 0000000000..e6709adebc
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SerialRangeSetTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * Tests for the set of serial number ranges.
+ */
+public class SerialRangeSetTest {
+
+ private SerialRangeSet ranges = new SerialRangeSet();
+
+ @Test
+ public void testInsertSimple() {
+ ranges.add(1);
+ ranges.add(3);
+ ranges.add(5);
+ assertEquals(3, ranges.size());
+ assertFalse(ranges.contains(0));
+ assertTrue(ranges.contains(1));
+ assertFalse(ranges.contains(2));
+ assertTrue(ranges.contains(3));
+ assertFalse(ranges.contains(4));
+ assertTrue(ranges.contains(5));
+ assertFalse(ranges.contains(6));
+ }
+
+ @Test
+ public void testInsertSimpleRanges() {
+ ranges.add(1, 2);
+ ranges.add(4, 5);
+ ranges.add(7, 8);
+ assertEquals(3, ranges.size());
+ assertFalse(ranges.contains(0));
+ assertTrue(ranges.contains(1));
+ assertTrue(ranges.contains(2));
+ assertFalse(ranges.contains(3));
+ assertTrue(ranges.contains(4));
+ assertTrue(ranges.contains(5));
+ assertFalse(ranges.contains(6));
+ assertTrue(ranges.contains(7));
+ assertTrue(ranges.contains(8));
+ assertFalse(ranges.contains(9));
+ }
+
+ @Test
+ public void testInsertCoalesce() {
+ ranges.add(5);
+ ranges.add(1);
+ ranges.add(2);
+ ranges.add(4);
+ ranges.add(7);
+ ranges.add(3);
+ assertEquals(2, ranges.size());
+ assertFalse(ranges.contains(0));
+ assertTrue(ranges.contains(1));
+ assertTrue(ranges.contains(2));
+ assertTrue(ranges.contains(3));
+ assertTrue(ranges.contains(4));
+ assertTrue(ranges.contains(5));
+ assertFalse(ranges.contains(6));
+ assertTrue(ranges.contains(7));
+ assertFalse(ranges.contains(8));
+ }
+
+ @Test
+ public void testInsertOverlap() {
+ ranges.add(1, 3);
+ ranges.add(6);
+ ranges.add(2, 5);
+ assertEquals(1, ranges.size());
+ assertFalse(ranges.contains(0));
+ assertTrue(ranges.contains(1));
+ assertTrue(ranges.contains(2));
+ assertTrue(ranges.contains(3));
+ assertTrue(ranges.contains(4));
+ assertTrue(ranges.contains(5));
+ assertTrue(ranges.contains(6));
+ assertFalse(ranges.contains(7));
+ }
+
+ @Test
+ public void testInsertOverlapMultiple() {
+ ranges.add(1, 3);
+ ranges.add(5, 6);
+ ranges.add(8);
+ ranges.add(2, 5);
+ assertEquals(2, ranges.size());
+ assertFalse(ranges.contains(0));
+ assertTrue(ranges.contains(1));
+ assertTrue(ranges.contains(2));
+ assertTrue(ranges.contains(3));
+ assertTrue(ranges.contains(4));
+ assertTrue(ranges.contains(5));
+ assertTrue(ranges.contains(6));
+ assertFalse(ranges.contains(7));
+ assertTrue(ranges.contains(8));
+ assertFalse(ranges.contains(9));
+ }
+
+ @Test
+ public void testInsertOverlapTotal() {
+ ranges.add(1, 3);
+ ranges.add(2, 3);
+ assertEquals(1, ranges.size());
+ assertFalse(ranges.contains(0));
+ assertTrue(ranges.contains(1));
+ assertTrue(ranges.contains(2));
+ assertTrue(ranges.contains(3));
+ assertFalse(ranges.contains(4));
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtilsTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtilsTest.java
new file mode 100644
index 0000000000..79ca21fa35
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtilsTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.InputStream;
+import java.security.PublicKey;
+import java.time.Instant;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for {@link SshCertificateUtils}. They use a certificate valid from
+ * 2024-09-01 00:00:00 to 2024-09-02 00:00:00 UTC.
+ */
+public class SshCertificateUtilsTest {
+
+ private OpenSshCertificate certificate;
+
+ @Before
+ public void loadCertificate() throws Exception {
+ try (InputStream in = this.getClass().getResourceAsStream(
+ "certs/expired.cert")) {
+ List<AuthorizedKeyEntry> keys = AuthorizedKeyEntry
+ .readAuthorizedKeys(in, true);
+ if (keys.isEmpty()) {
+ certificate = null;
+ }
+ PublicKey key = keys.get(0).resolvePublicKey(null,
+ PublicKeyEntryResolver.FAILING);
+ assertTrue(
+ "Expected an OpenSshKeyCertificate but got a "
+ + key.getClass().getName(),
+ key instanceof OpenSshCertificate);
+ certificate = (OpenSshCertificate) key;
+ }
+ }
+
+ @Test
+ public void testValidUserCertificate() {
+ assertNull(SshCertificateUtils.verify(certificate, null));
+ Instant validTime = Instant.parse("2024-09-01T00:00:00.00Z");
+ assertNull(SshCertificateUtils.verify(certificate, validTime));
+ assertNull(SshCertificateUtils.checkExpiration(certificate, validTime));
+ }
+
+ @Test
+ public void testCheckTooEarly() {
+ Instant invalidTime = Instant.parse("2024-08-31T23:59:59.00Z");
+ assertNotNull(
+ SshCertificateUtils.checkExpiration(certificate, invalidTime));
+ assertNotNull(SshCertificateUtils.verify(certificate, invalidTime));
+ }
+
+ @Test
+ public void testCheckExpired() {
+ Instant invalidTime = Instant.parse("2024-09-02T00:00:01.00Z");
+ assertNotNull(
+ SshCertificateUtils.checkExpiration(certificate, invalidTime));
+ assertNotNull(SshCertificateUtils.verify(certificate, invalidTime));
+ }
+
+ @Test
+ public void testInvalidSignature() throws Exception {
+ // Modify the serialized certificate, then re-load it again. To check that
+ // serialization per se works fine, also check an unmodified version.
+ Buffer buffer = new ByteArrayBuffer();
+ buffer.putPublicKey(certificate);
+ int pos = buffer.rpos();
+ PublicKey unchanged = buffer.getPublicKey();
+ assertTrue(
+ "Expected an OpenSshCertificate but got a "
+ + unchanged.getClass().getName(),
+ unchanged instanceof OpenSshCertificate);
+ assertNull(SshCertificateUtils.verify((OpenSshCertificate) unchanged,
+ null));
+ buffer.rpos(pos);
+ // Change a byte. The test certificate has the key ID at offset 128.
+ // Changing a byte in the key ID should still result in a successful
+ // deserialization, but then fail the signature check.
+ buffer.array()[pos + 128]++;
+ PublicKey changed = buffer.getPublicKey();
+ assertTrue(
+ "Expected an OpenSshCertificate but got a "
+ + changed.getClass().getName(),
+ changed instanceof OpenSshCertificate);
+ assertNotNull(
+ SshCertificateUtils.verify((OpenSshCertificate) changed, null));
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifierTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifierTest.java
new file mode 100644
index 0000000000..e5dfe497ca
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifierTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+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.assertTrue;
+
+import java.util.Map;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.VerificationResult;
+import org.eclipse.jgit.lib.SignatureVerifier;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.StringUtils;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link SshSignatureVerifier}.
+ */
+public class SshSignatureVerifierTest extends AbstractSshSignatureTest {
+
+ @Test
+ public void testPlainSignature() throws Exception {
+ RevCommit c = checkSshSignature(
+ createSignedCommit(null, "signing_key.pub"));
+ try (Git git = new Git(db)) {
+ Map<String, VerificationResult> results = git.verifySignature()
+ .addName(c.getName()).call();
+ assertEquals(1, results.size());
+ VerificationResult verified = results.get(c.getName());
+ assertNotNull(verified);
+ assertNull(verified.getException());
+ SignatureVerifier.SignatureVerification v = verified
+ .getVerification();
+ assertTrue(v.verified());
+ assertFalse(v.expired());
+ assertTrue(StringUtils.isEmptyOrNull(v.message()));
+ assertEquals("tester@example.com", v.keyUser());
+ assertEquals("SHA256:GKW0xy+XKnJGs0CJqP6j5bd4FdiwWNaUbwvUbHvhQKo",
+ v.keyFingerprint());
+ assertEquals(SignatureVerifier.TrustLevel.FULL, v.trustLevel());
+ assertEquals(commitTime, v.creationDate().toInstant());
+ }
+ }
+
+ @Test
+ public void testCertificateSignature() throws Exception {
+ RevCommit c = checkSshSignature(
+ createSignedCommit("tester.cert", "signing_key-cert.pub"));
+ try (Git git = new Git(db)) {
+ Map<String, VerificationResult> results = git.verifySignature()
+ .addName(c.getName()).call();
+ assertEquals(1, results.size());
+ VerificationResult verified = results.get(c.getName());
+ assertNotNull(verified);
+ assertNull(verified.getException());
+ SignatureVerifier.SignatureVerification v = verified
+ .getVerification();
+ assertTrue(v.verified());
+ assertFalse(v.expired());
+ assertTrue(StringUtils.isEmptyOrNull(v.message()));
+ assertEquals("tester@example.com", v.keyUser());
+ assertEquals("SHA256:GKW0xy+XKnJGs0CJqP6j5bd4FdiwWNaUbwvUbHvhQKo",
+ v.keyFingerprint());
+ assertEquals(SignatureVerifier.TrustLevel.FULL, v.trustLevel());
+ assertEquals(commitTime, v.creationDate().toInstant());
+ }
+ }
+
+ @Test
+ public void testNoPrincipalsSignature() throws Exception {
+ RevCommit c = checkSshSignature(createSignedCommit("no_principals.cert",
+ "signing_key-cert.pub"));
+ try (Git git = new Git(db)) {
+ Map<String, VerificationResult> results = git.verifySignature()
+ .addName(c.getName()).call();
+ assertEquals(1, results.size());
+ VerificationResult verified = results.get(c.getName());
+ assertNotNull(verified);
+ assertNull(verified.getException());
+ SignatureVerifier.SignatureVerification v = verified
+ .getVerification();
+ assertFalse(v.verified());
+ assertFalse(v.expired());
+ assertNull(v.keyUser());
+ assertEquals("SHA256:GKW0xy+XKnJGs0CJqP6j5bd4FdiwWNaUbwvUbHvhQKo",
+ v.keyFingerprint());
+ assertEquals(SignatureVerifier.TrustLevel.NEVER, v.trustLevel());
+ assertTrue(v.message().contains("*@example.com"));
+ assertEquals(commitTime, v.creationDate().toInstant());
+ }
+ }
+
+ @Test
+ public void testOtherCertificateSignature() throws Exception {
+ RevCommit c = checkSshSignature(
+ createSignedCommit("other.cert", "signing_key-cert.pub"));
+ try (Git git = new Git(db)) {
+ Map<String, VerificationResult> results = git.verifySignature()
+ .addName(c.getName()).call();
+ assertEquals(1, results.size());
+ VerificationResult verified = results.get(c.getName());
+ assertNotNull(verified);
+ assertNull(verified.getException());
+ SignatureVerifier.SignatureVerification v = verified
+ .getVerification();
+ assertTrue(v.verified());
+ assertFalse(v.expired());
+ assertTrue(StringUtils.isEmptyOrNull(v.message()));
+ assertEquals("other@example.com", v.keyUser());
+ assertEquals("SHA256:GKW0xy+XKnJGs0CJqP6j5bd4FdiwWNaUbwvUbHvhQKo",
+ v.keyFingerprint());
+ assertEquals(SignatureVerifier.TrustLevel.FULL, v.trustLevel());
+ assertEquals(commitTime, v.creationDate().toInstant());
+ }
+ }
+
+ @Test
+ public void testTwoPrincipalsCertificateSignature() throws Exception {
+ RevCommit c = checkSshSignature(createSignedCommit(
+ "two_principals.cert", "signing_key-cert.pub"));
+ try (Git git = new Git(db)) {
+ Map<String, VerificationResult> results = git.verifySignature()
+ .addName(c.getName()).call();
+ assertEquals(1, results.size());
+ VerificationResult verified = results.get(c.getName());
+ assertNotNull(verified);
+ assertNull(verified.getException());
+ SignatureVerifier.SignatureVerification v = verified
+ .getVerification();
+ assertTrue(v.verified());
+ assertFalse(v.expired());
+ assertTrue(StringUtils.isEmptyOrNull(v.message()));
+ assertEquals("foo@example.com,tester@example.com", v.keyUser());
+ assertEquals("SHA256:GKW0xy+XKnJGs0CJqP6j5bd4FdiwWNaUbwvUbHvhQKo",
+ v.keyFingerprint());
+ assertEquals(SignatureVerifier.TrustLevel.FULL, v.trustLevel());
+ assertEquals(commitTime, v.creationDate().toInstant());
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignerTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignerTest.java
new file mode 100644
index 0000000000..b3a4482d23
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignerTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+/**
+ * Tests for the {@link SshSigner}.
+ */
+public class SshSignerTest extends AbstractSshSignatureTest {
+
+ @Test
+ public void testPlainSignature() throws Exception {
+ checkSshSignature(createSignedCommit(null, "signing_key.pub"));
+ }
+
+ @Test
+ public void testExpiredSignature() throws Exception {
+ Throwable t = assertThrows(Throwable.class,
+ () -> createSignedCommit("expired.cert",
+ "signing_key-cert.pub"));
+ // The exception or one of its causes should mention "[Ee]xpired" and
+ // "[Cc]ertificate" in the message
+ while (t != null) {
+ String message = t.getMessage();
+ if (message.contains("xpired") && message.contains("ertificate")) {
+ return;
+ }
+ t = t.getCause();
+ }
+ fail("Expected exception message not found");
+ }
+
+ @Test
+ public void testCertificateSignature() throws Exception {
+ checkSshSignature(createSignedCommit("tester.cert", "signing_key.pub"));
+ }
+
+ @Test
+ public void testNoPrincipalsSignature() throws Exception {
+ // Certificate has no principals; should still work
+ checkSshSignature(
+ createSignedCommit("no_principals.cert", "signing_key.pub"));
+ }
+
+ @Test
+ public void testOtherSignature() throws Exception {
+ // Certificate has a principal different that tester@example.com; should
+ // still work
+ checkSshSignature(createSignedCommit("other.cert", "signing_key.pub"));
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/VerifyGitSignaturesTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/VerifyGitSignaturesTest.java
new file mode 100644
index 0000000000..30ddee559c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/VerifyGitSignaturesTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.VerificationResult;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.SignatureVerifier;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.util.GitDateFormatter;
+import org.eclipse.jgit.util.SignatureUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Verifies signatures made with C git and OpenSSH 9.0 to ensure we arrive at
+ * the same good/bad decisions, and that we can verify signatures not created by
+ * ourselves.
+ * <p>
+ * Clones a JGit repo from a git bundle file created with C git, then checks all
+ * the commits and their signatures. (All commits in that bundle have SSH
+ * signatures.)
+ * </p>
+ */
+public class VerifyGitSignaturesTest extends LocalDiskRepositoryTestCase {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(VerifyGitSignaturesTest.class);
+
+ @Rule
+ public TemporaryFolder bundleDir = new TemporaryFolder();
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ try (InputStream in = this.getClass()
+ .getResourceAsStream("repo.bundle")) {
+ Files.copy(in, bundleDir.getRoot().toPath().resolve("repo.bundle"));
+ }
+ try (InputStream in = this.getClass()
+ .getResourceAsStream("allowed_signers")) {
+ Files.copy(in,
+ bundleDir.getRoot().toPath().resolve("allowed_signers"));
+ }
+ }
+
+ /**
+ * Tests signatures created by C git using OpenSSH 9.0.
+ */
+ @Test
+ public void testGitSignatures() throws Exception {
+ File gitDir = new File(getTemporaryDirectory(), "repo.git");
+ try (Git git = Git.cloneRepository().setBare(true)
+ .setGitDir(gitDir)
+ .setURI(new File(bundleDir.getRoot(), "repo.bundle").toURI()
+ .toString())
+ .setBranch("master")
+ .call()) {
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("gpg", "ssh", "allowedSignersFile",
+ bundleDir.getRoot().toPath().resolve("allowed_signers")
+ .toAbsolutePath().toString().replace('\\', '/'));
+ config.save();
+ List<String> commits = new ArrayList<>();
+ Map<String, PersonIdent> committers = new HashMap<>();
+ git.log().all().call().forEach(c -> {
+ commits.add(c.getName());
+ committers.put(c.getName(), c.getCommitterIdent());
+ });
+ Map<String, Boolean> expected = new HashMap<>();
+ // These two commits do have multiple principals. GIT just reports
+ // the first one; we report both.
+ expected.put("9f79a7b661a22ab1ddf8af880d23678ae7696b71",
+ Boolean.TRUE);
+ expected.put("435108d157440e77d61a914b6a5736bc831c874d",
+ Boolean.TRUE);
+ // This commit has a wrong commit message; the certificate used
+ // did _not_ have two principals, but only a single principal
+ // foo@example.org.
+ expected.put("779dac7de40ebc3886af87d5e6680a09f8b13a3e",
+ Boolean.TRUE);
+ // Signed with other_key-cert.pub: we still don't know the key,
+ // but we do know the certificate's CA key, and trust it, so it's
+ // accepted as a signature from the principal(s) listed in the
+ // certificate.
+ expected.put("951f06d5b5598b721b98d98b04e491f234c1926a",
+ Boolean.TRUE);
+ // Signature with other_key.pub not listed in allowed_signers
+ expected.put("984e629c6d543a7f77eb49a8c9316f2ae4416375",
+ Boolean.FALSE);
+ // Signed with other-ca.cert (CA key not in allowed_signers), but
+ // the certified key _is_ listed in allowed_signers.
+ expected.put("1d7ac6d91747a9c9a777df238fbdaeffa7731a6c",
+ Boolean.FALSE);
+ expected.put("a297bcfbf5c4a850f9770655fef7315328a4b3fb",
+ Boolean.TRUE);
+ expected.put("852729d54676cb83826ed821dc7734013e97950d",
+ Boolean.TRUE);
+ // Signature with a certificate without principals.
+ expected.put("e39a049f75fe127eb74b30aba4b64e171d4281dd",
+ Boolean.FALSE);
+ // Signature made with expired.cert (expired at the commit time).
+ // git/OpenSSH 9.0 allows to create such signatures, but reports
+ // them as FALSE. Our SshSigner doesn't allow creating such
+ // signatures.
+ expected.put("303ea5e61feacdad4cb012b4cb6b0cea3fbcef9f",
+ Boolean.FALSE);
+ expected.put("1ae4b120a869b72a7a2d4ad4d7a8c9d454384333",
+ Boolean.TRUE);
+ Map<String, VerificationResult> results = git.verifySignature()
+ .addNames(commits).call();
+ GitDateFormatter dateFormat = new GitDateFormatter(
+ GitDateFormatter.Format.ISO);
+ for (String oid : commits) {
+ VerificationResult v = results.get(oid);
+ assertNotNull(v);
+ assertNull(v.getException());
+ SignatureVerifier.SignatureVerification sv = v
+ .getVerification();
+ assertNotNull(sv);
+ LOG.info("Commit {}\n{}", oid, SignatureUtils.toString(sv,
+ committers.get(oid), dateFormat));
+ Boolean wanted = expected.get(oid);
+ assertNotNull(wanted);
+ assertEquals(wanted, Boolean.valueOf(sv.verified()));
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReaderTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReaderTest.java
new file mode 100644
index 0000000000..d36c38f335
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReaderTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.apache.sshd.client.config.hosts.KnownHostEntry;
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.junit.Test;
+
+public class KnownHostEntryReaderTest {
+
+ @Test
+ public void testUnsupportedHostKeyLine() {
+ KnownHostEntry entry = KnownHostEntryReader.parseHostEntry(
+ "[localhost]:2222 ssh-unknown AAAAC3NzaC1lZDI1NTE5AAAAIPu6ntmyfSOkqLl3qPxD5XxwW7OONwwSG3KO+TGn+PFu");
+ AuthorizedKeyEntry keyEntry = entry.getKeyEntry();
+ assertNotNull(keyEntry);
+ assertEquals("ssh-unknown", keyEntry.getKeyType());
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabaseTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabaseTest.java
new file mode 100644
index 0000000000..6b61821a0f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabaseTest.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2025 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.certificate.OpenSshCertificateBuilder;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.transport.CredentialItem;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.sshd.ServerKeyDatabase;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Tests for {@link OpenSshServerKeyDatabase}.
+ */
+public class OpenSshServerKeyDatabaseTest {
+
+ private static final InetSocketAddress LOCAL = new InetSocketAddress(
+ InetAddress.getLoopbackAddress(), SshConstants.DEFAULT_PORT);
+
+ private static final InetSocketAddress LOCAL_29418 = new InetSocketAddress(
+ InetAddress.getLoopbackAddress(), 29418);
+
+ private static PublicKey rsa1024;
+ private static PublicKey rsa2048;
+ private static PublicKey ec256;
+ private static PublicKey ec384;
+ private static PublicKey caKey;
+ private static PublicKey certificate;
+
+ @BeforeClass
+ public static void initKeys() throws Exception {
+ // Generate a few keys that we can use
+ KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(KeyUtils.RSA_ALGORITHM);
+ gen.initialize(1024);
+ rsa1024 = gen.generateKeyPair().getPublic();
+ gen.initialize(2048);
+ rsa2048 = gen.generateKeyPair().getPublic();
+ gen = SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
+ ECCurves curve = ECCurves.fromCurveSize(256);
+ gen.initialize(curve.getParameters());
+ ec256 = gen.generateKeyPair().getPublic();
+ PublicKey certKey = gen.generateKeyPair().getPublic();
+ curve = ECCurves.fromCurveSize(384);
+ gen.initialize(curve.getParameters());
+ ec384 = gen.generateKeyPair().getPublic();
+ // Generate a certificate for some key
+ gen.initialize(curve.getParameters());
+ KeyPair ca = gen.generateKeyPair();
+ caKey = ca.getPublic();
+ certificate = OpenSshCertificateBuilder
+ .hostCertificate()
+ .serial(System.currentTimeMillis())
+ .publicKey(certKey)
+ .id("test-host-cert")
+ .validBefore(
+ System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1))
+ .principals(List.of("localhost", "127.0.0.1"))
+ .sign(ca, "ecdsa-sha2-nistp384");
+ }
+
+ @Rule
+ public TemporaryFolder tmp = new TemporaryFolder();
+
+ private Path knownHosts;
+ private Path knownHosts2;
+ private ServerKeyDatabase database;
+
+ @Before
+ public void setupDatabase() throws Exception {
+ Path root = tmp.getRoot().toPath();
+ knownHosts = root.resolve("known_hosts");
+ knownHosts2 = root.resolve("known_hosts2");
+ database = new OpenSshServerKeyDatabase(false, List.of(knownHosts, knownHosts2));
+ }
+
+ @Test
+ public void testFindInSecondFile() throws Exception {
+ Files.write(knownHosts,
+ List.of(
+ "some.other.host " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384)));
+ Files.write(knownHosts2,
+ List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048)));
+ assertTrue(database.accept("localhost", LOCAL, ec256,
+ new KnownHostsConfig(), null));
+ assertFalse(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testNoFirstFile() throws Exception {
+ Files.write(knownHosts2,
+ List.of("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048)));
+ assertTrue(database.accept("localhost", LOCAL, ec256,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testFind() throws Exception {
+ Files.write(knownHosts,
+ List.of("localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384)));
+ Files.write(knownHosts2,
+ List.of("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048)));
+ assertTrue(database.accept("localhost", LOCAL, ec256,
+ new KnownHostsConfig(), null));
+ assertTrue(database.accept("localhost:22", LOCAL, ec256,
+ new KnownHostsConfig(), null));
+ assertTrue(database.accept("127.0.0.1", LOCAL, rsa1024,
+ new KnownHostsConfig(), null));
+ assertTrue(database.accept("[127.0.0.1]:22", LOCAL, rsa1024,
+ new KnownHostsConfig(), null));
+ assertFalse(database.accept("localhost:29418", LOCAL_29418, ec256,
+ new KnownHostsConfig(), null));
+ assertFalse(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testFindCertificate() throws Exception {
+ Files.write(knownHosts,
+ List.of("localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384),
+ "@cert-authority localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(caKey)));
+ assertTrue(database.accept("localhost", LOCAL, certificate,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testCaKeyNotConsidered() throws Exception {
+ Files.write(knownHosts,
+ List.of("localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384),
+ "@cert-authority localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(ec256)));
+ assertFalse(database.accept("localhost", LOCAL, ec256,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testkeyPlainAndCa() throws Exception {
+ Files.write(knownHosts, List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384),
+ "@cert-authority localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(ec256),
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256)));
+ // ec256 is a CA key, but also a valid direct host key for localhost
+ assertTrue(database.accept("localhost", LOCAL, ec256,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testLookupCertificate() throws Exception {
+ List<PublicKey> keys = database.lookup("localhost", LOCAL,
+ new KnownHostsConfig());
+ // Certificates or CA keys are not reported via lookup.
+ assertTrue(keys.isEmpty());
+ }
+
+ @Test
+ public void testCertificateNotAdded() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ assertFalse(database.accept("localhost", LOCAL, certificate,
+ new KnownHostsConfig(), null));
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertFalse(
+ database.accept("localhost", LOCAL, certificate,
+ new KnownHostsConfig(
+ KnownHostsConfig.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(0, ui.invocations);
+ assertFile(knownHosts, initialKnownHosts);
+ }
+
+ @Test
+ public void testCertificateNotModified() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "@cert-authority localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(ec384),
+ "some.other.com " + PublicKeyEntry.toString(ec256));
+ Files.write(knownHosts, initialKnownHosts);
+ assertFalse(database.accept("localhost", LOCAL, certificate,
+ new KnownHostsConfig(), null));
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertFalse(
+ database.accept("localhost", LOCAL, certificate,
+ new KnownHostsConfig(
+ KnownHostsConfig.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(0, ui.invocations);
+ assertFile(knownHosts, initialKnownHosts);
+ }
+
+ @Test
+ public void testModifyFile() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "some.other.host " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ assertFalse(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertFalse(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ACCEPT_NEW),
+ null));
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ACCEPT_ANY),
+ null));
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertFalse(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ null));
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2, initialKnownHosts2);
+
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, false);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2, initialKnownHosts2);
+
+ ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2, List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048)));
+ assertTrue(database.accept("127.0.0.1", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testModifyFirstFile() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List.of(
+ "some.other.host " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts,
+ List.of("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384),
+ "some.other.com " + PublicKeyEntry.toString(ec384)));
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testModifyMatchingKeyType() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, rsa2048,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts,
+ List.of("localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(rsa2048),
+ "some.other.com " + PublicKeyEntry.toString(ec384)));
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, rsa2048,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testModifyMatchingKeyType2() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts,
+ List.of("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384),
+ "some.other.com " + PublicKeyEntry.toString(ec384)));
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testModifySecondFile() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2,
+ List.of("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048)));
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testAddNewKey() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "some.other.host " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec256));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List
+ .of("some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ List<String> expected = new ArrayList<>(initialKnownHosts);
+ expected.add("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384));
+ assertFile(knownHosts, expected);
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testCreateNewFile() throws Exception {
+ List<String> initialKnownHosts2 = List
+ .of("some.other.com " + PublicKeyEntry.toString(ec256));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts, List
+ .of("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384)));
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testAddNewKey2() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "some.other.host " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec256));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List
+ .of("some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("127.0.0.1:29418", LOCAL_29418, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ List<String> expected = new ArrayList<>(initialKnownHosts);
+ expected.add("[127.0.0.1]:29418,[localhost]:29418 "
+ + PublicKeyEntry.toString(ec384));
+ assertFile(knownHosts, expected);
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("localhost:29418", LOCAL_29418, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testAddNewKey3() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "some.other.host " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec256));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List
+ .of("some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost:29418", LOCAL_29418, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ List<String> expected = new ArrayList<>(initialKnownHosts);
+ expected.add("[localhost]:29418,[127.0.0.1]:29418 "
+ + PublicKeyEntry.toString(ec384));
+ assertFile(knownHosts, expected);
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("127.0.0.1:29418", LOCAL_29418, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testUnknownKeyType() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384)
+ .replace("ecdsa", "foo"),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ // The "modified key" dialog has two questions; whereas the "add new
+ // key" is just a simple question.
+ assertEquals(2, ui.questions);
+ List<String> expected = new ArrayList<>(initialKnownHosts);
+ expected.add("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384));
+ assertFile(knownHosts, expected);
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ private void assertFile(Path path, List<String> lines) throws Exception {
+ assertEquals(lines, Files.readAllLines(path).stream()
+ .filter(s -> !s.isBlank()).toList());
+ }
+
+ private static class TestCredentialsProvider extends CredentialsProvider {
+
+ private final boolean[] values;
+
+ int invocations = 0;
+
+ int questions = 0;
+
+ TestCredentialsProvider(boolean accept, boolean store) {
+ values = new boolean[] { accept, store };
+ }
+
+ @Override
+ public boolean isInteractive() {
+ return true;
+ }
+
+ @Override
+ public boolean supports(CredentialItem... items) {
+ return true;
+ }
+
+ @Override
+ public boolean get(URIish uri, CredentialItem... items)
+ throws UnsupportedCredentialItem {
+ invocations++;
+ int i = 0;
+ for (CredentialItem item : items) {
+ if (item instanceof CredentialItem.YesNoType) {
+ ((CredentialItem.YesNoType) item)
+ .setValue(i < values.length && values[i++]);
+ questions++;
+ }
+ }
+ return true;
+ }
+ }
+
+ private static class KnownHostsConfig implements ServerKeyDatabase.Configuration {
+
+ @NonNull
+ private final StrictHostKeyChecking check;
+
+ KnownHostsConfig() {
+ this(StrictHostKeyChecking.REQUIRE_MATCH);
+ }
+
+ KnownHostsConfig(@NonNull StrictHostKeyChecking check) {
+ this.check = check;
+ }
+
+ @Override
+ public List<String> getUserKnownHostsFiles() {
+ return List.of();
+ }
+
+ @Override
+ public List<String> getGlobalKnownHostsFiles() {
+ return List.of();
+ }
+
+ @Override
+ public StrictHostKeyChecking getStrictHostKeyChecking() {
+ return check;
+ }
+
+ @Override
+ public boolean getHashKnownHosts() {
+ return false;
+ }
+
+ @Override
+ public String getUsername() {
+ return "user";
+ }
+
+ }
+}
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
index eef0402b07..69bd5c501a 100644
--- 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
@@ -17,7 +17,6 @@ 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;
@@ -52,7 +51,8 @@ public class ApacheSshProtocol2Test extends SshBasicTestBase {
@Override
public void setUp() throws Exception {
super.setUp();
- StoredConfig config = ((Repository) db).getConfig();
+ @SuppressWarnings("restriction")
+ StoredConfig config = db.getConfig();
config.setInt("protocol", null, "version", 2);
config.save();
}
diff --git a/org.eclipse.jgit.ssh.apache/BUILD b/org.eclipse.jgit.ssh.apache/BUILD
index fd88a8a88a..83709c35cb 100644
--- a/org.eclipse.jgit.ssh.apache/BUILD
+++ b/org.eclipse.jgit.ssh.apache/BUILD
@@ -12,7 +12,6 @@ java_library(
resource_strip_prefix = "org.eclipse.jgit.ssh.apache/resources",
resources = RESOURCES,
deps = [
- "//lib:eddsa",
"//lib:slf4j-api",
"//lib:sshd-osgi",
"//lib:sshd-sftp",
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
index 02d94662ee..f38ea723b2 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
@@ -6,9 +6,10 @@ Bundle-SymbolicName: org.eclipse.jgit.ssh.apache
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-ActivationPolicy: lazy
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Export-Package: org.eclipse.jgit.internal.transport.sshd;version="7.0.0";x-internal:=true;
+Export-Package: org.eclipse.jgit.internal.signing.ssh;version="7.3.0";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.internal.transport.sshd;version="7.3.0";x-friends:="org.eclipse.jgit.ssh.apache.test";
uses:="org.apache.sshd.client,
org.apache.sshd.client.auth,
org.apache.sshd.client.auth.keyboard,
@@ -23,78 +24,79 @@ Export-Package: org.eclipse.jgit.internal.transport.sshd;version="7.0.0";x-inter
org.apache.sshd.common.signature,
org.apache.sshd.common.util.buffer,
org.eclipse.jgit.transport",
- org.eclipse.jgit.internal.transport.sshd.agent;version="7.0.0";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.auth;version="7.0.0";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.pkcs11;version="7.0.0";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.proxy;version="7.0.0";x-friends:="org.eclipse.jgit.ssh.apache.test",
- org.eclipse.jgit.transport.sshd;version="7.0.0";
+ org.eclipse.jgit.internal.transport.sshd.agent;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.auth;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.pkcs11;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="7.3.0";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.signing.ssh;version="7.3.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.transport.sshd;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.apache.sshd.client.config.hosts,
org.apache.sshd.common.keyprovider,
org.eclipse.jgit.util,
org.apache.sshd.client.session,
org.apache.sshd.client.keyverifier",
- org.eclipse.jgit.transport.sshd.agent;version="7.0.0",
- sun.security.x509
-Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
- org.apache.sshd.agent;version="[2.12.0,2.13.0)",
- org.apache.sshd.client;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.auth;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.auth.keyboard;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.auth.password;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.auth.pubkey;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.channel;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.config.hosts;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.config.keys;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.future;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.keyverifier;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.session;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.session.forward;version="[2.12.0,2.13.0)",
- org.apache.sshd.common;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.auth;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.channel;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.cipher;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.compression;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.config.keys;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.config.keys.loader;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.config.keys.loader.openssh.kdf;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.config.keys.u2f;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.digest;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.forward;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.future;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.helpers;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.io;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.kex;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.kex.extension;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.kex.extension.parser;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.keyprovider;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.mac;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.random;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.session;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.session.helpers;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.signature;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.buffer;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.buffer.keys;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.closeable;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.io;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.io.der;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.io.functors;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.io.resource;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.logging;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.net;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.security;version="[2.12.0,2.13.0)",
- org.apache.sshd.core;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.auth;version="[2.12.0,2.13.0)",
- org.apache.sshd.sftp;version="[2.12.0,2.13.0)",
- org.apache.sshd.sftp.client;version="[2.12.0,2.13.0)",
- org.apache.sshd.sftp.common;version="[2.12.0,2.13.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.fnmatch;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.transport.sshd.agent;version="7.3.0"
+Import-Package: org.apache.sshd.agent;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.auth;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.auth.keyboard;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.auth.password;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.auth.pubkey;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.channel;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.config.hosts;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.config.keys;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.future;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.keyverifier;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.session;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.session.forward;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.auth;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.channel;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.cipher;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.compression;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.config.keys;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.config.keys.loader;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.config.keys.loader.openssh.kdf;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.config.keys.u2f;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.digest;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.forward;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.future;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.helpers;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.io;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.kex;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.kex.extension;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.kex.extension.parser;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.keyprovider;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.mac;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.random;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.session;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.session.helpers;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.signature;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.buffer;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.buffer.keys;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.closeable;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.io;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.io.der;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.io.functors;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.io.resource;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.logging;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.net;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.security;version="[2.15.0,2.16.0)",
+ org.apache.sshd.core;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.auth;version="[2.15.0,2.16.0)",
+ org.apache.sshd.sftp;version="[2.15.0,2.16.0)",
+ org.apache.sshd.sftp.client;version="[2.15.0,2.16.0)",
+ org.apache.sshd.sftp.common;version="[2.15.0,2.16.0)",
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.fnmatch;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.slf4j;version="[1.7.0,3.0.0)"
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
index bb0d79b3d5..d6225e27db 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.ssh.apache - Sources
Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml
index 7cc3a55b26..365034a9c1 100644
--- a/org.eclipse.jgit.ssh.apache/pom.xml
+++ b/org.eclipse.jgit.ssh.apache/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ssh.apache</artifactId>
@@ -30,7 +30,6 @@
<properties>
<translate-qualifier/>
<source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
- <eddsa-version>0.3.0</eddsa-version>
</properties>
<dependencies>
@@ -63,12 +62,6 @@
</dependency>
<dependency>
- <groupId>net.i2p.crypto</groupId>
- <artifactId>eddsa</artifactId>
- <version>${eddsa-version}</version>
- </dependency>
-
- <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
diff --git a/org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignatureVerifierFactory b/org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignatureVerifierFactory
new file mode 100644
index 0000000000..4a0f553c81
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignatureVerifierFactory
@@ -0,0 +1 @@
+org.eclipse.jgit.signing.ssh.SshSignatureVerifierFactory \ No newline at end of file
diff --git a/org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory b/org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory
new file mode 100644
index 0000000000..80f22c055f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory
@@ -0,0 +1 @@
+org.eclipse.jgit.signing.ssh.SshSignerFactory \ No newline at end of file
diff --git a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
index 7da7181887..773c4b9432 100644
--- a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
+++ b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
@@ -61,6 +61,7 @@ The expected {1} key for host ''{2}'' has the fingerprints:\n\
The {0} key actually received has the fingerprints:\n\
{5}\n\
{6}
+knownHostsRevokedCertificateMsg=Host ''{0}'' sent a certificate with a CA key that is marked as revoked in the known hosts file {1}.
knownHostsRevokedKeyMsg=Host ''{0}'' sent a key that is marked as revoked in the known hosts file {1}.
knownHostsUnknownKeyMsg=The authenticity of host ''{0}'' cannot be established.
knownHostsUnknownKeyPrompt=Accept and store this key, and continue connecting?
@@ -125,4 +126,69 @@ sshClosingDown=Apache MINA sshd session factory is closing down; cannot create n
sshCommandTimeout={0} timed out after {1} seconds while opening the channel
sshProcessStillRunning={0} is not yet completed, cannot get exit code
sshProxySessionCloseFailed=Error while closing proxy session {0}
+signAllowedSignersCertAuthorityError=Garbage after cert-authority
+signAllowedSignersEmptyIdentity=Identities contains an empty identity; check for spurious extra commas: {0}
+signAllowedSignersEmptyNamespaces=Empty namespaces= is not allowed; to allow a key for any namespace, omit the namespaces option
+signAllowedSignersFormatError=Cannot parse allowed signers file {0}, problem at line {1}: {2}
+signAllowedSignersInvalidDate=Cannot parse valid-before or valid-after date {0}
+signAllowedSignersLineFormat=Invalid line format
+signAllowedSignersMultiple={0} is allowed only once
+signAllowedSignersNoIdentities=Line has no identity patterns
+signAllowedSignersPublicKeyParsing=Cannot parse public key {0}
+signAllowedSignersUnterminatedQuote=Unterminated double quote
+signCertAlgorithmMismatch=Certificate of type {0} with CA key {1} uses an incompatible signature algorithm {2}
+signCertAlgorithmUnknown=Certificate with CA key {0} is signed with an unknown algorithm {1}
+signCertificateExpired=Expired certificate with CA key {0}
+signCertificateInvalid=Certificate signature does not match on certificate with CA key {0}
+signCertificateNotForName=Certificate with CA key {0} does not apply for name ''{1}''
+signCertificateRevoked=Certificate with CA key {0} was revoked
+signCertificateTooEarly=Certificate with CA key {0} was not valid yet
+signCertificateWithoutPrincipals=Certificate with CA key {0} has no principals; identities from gpg.ssh.allowedSignersFile: {1}
+signDefaultKeyEmpty=git.ssh.defaultKeyCommand {0} returned no key
+signDefaultKeyFailed=git.ssh.defaultKeyCommand {0} failed with exit code {1}\n{2}
+signDefaultKeyInterrupted=git.ssh.defaultKeyCommand {0} was interrupted
+signGarbageAtEnd=SSH signature has extra bytes at the end
+signInvalidAlgorithm=SSH signature has invalid signature algorithm {0}
+signInvalidKeyDSA=SSH signatures with DSA keys or certificates are not supported; use a different signing key.
+signInvalidMagic=SSH signature does not start with "SSHSIG"
+signInvalidNamespace=Namespace of SSH signature should be ''git'' but is ''{0}''
+signInvalidSignature=SSH signature is invalid: {0}
+signInvalidVersion=Cannot verify signature with version {0}
+signKeyExpired=Expired key used for SSH signature
+signKeyRevoked=Key used for the SSH signature was revoked
+signKeyTooEarly=Key used for the SSH signature was not valid yet
+signKrlBlobLeftover=gpg.ssh.revocationFile has invalid blob section {0} with {1} leftover bytes
+signKrlBlobLengthInvalid=gpg.ssh.revocationFile has invalid blob length {1} in section {0}
+signKrlBlobLengthInvalidExpected=gpg.ssh.revocationFile has invalid blob length {1} (expected {2}) in section {0}
+signKrlCaKeyLengthInvalid=gpg.ssh.revocationFile has invalid CA key length {0} in certificates section
+signKrlCertificateLeftover=gpg.ssh.revocationFile has invalid certificates section with {0} leftover bytes
+signKrlCertificateSubsectionLeftover=gpg.ssh.revocationFile has invalid certificates subsection with {0} leftover bytes
+signKrlCertificateSubsectionLength=gpg.ssh.revocationFile has invalid certificates subsection length {0}
+signKrlEmptyRange=gpg.ssh.revocationFile has an empty range of certificate serial numbers
+signKrlInvalidBitSetLength=gpg.ssh.revocationFile has invalid certificate serial number bit set length {0}
+signKrlInvalidKeyIdLength=gpg.ssh.revocationFile has invalid certificate key ID length {0}
+signKrlInvalidMagic=gpg.ssh.revocationFile is not a binary OpenSSH key revocation list
+signKrlInvalidReservedLength=gpg.ssh.revocationFile has an invalid reserved string length {0}
+signKrlInvalidVersion=gpg.ssh.revocationFile: cannot read KRLs with FORMAT_VERSION {0}
+signKrlNoCertificateSubsection=gpg.ssh.revocationFile has certificate section without subsections
+signKrlSerialZero=gpg.ssh.revocationFile: certificate serial number zero cannot be revoked
+signKrlShortRange=gpg.ssh.revocationFile: short certificate serial number range, need at least 8 more bytes, got only {0}
+signKrlUnknownSection=gpg.ssh.revocationFile has an unknown section type {0}
+signKrlUnknownSubsection=gpg.ssh.revocationFile has an unknown certificates subsection type {0}
+signLogFailure=SSH signature verification failed
+signMismatchedSignatureAlgorithm=SSH signature made with an ''{0}'' key has incompatible signature algorithm ''{1}''
+signNoAgent=No connector for ssh-agent found; maybe include org.eclipse.jgit.ssh.apache.agent in the application.
+signNoPrincipalMatched=No principal matched in gpg.ssh.allowedSignersFile
+signNoPublicKey=No public key found with signing key {0}
+signNoSigningKey=Git config user.signingKey or gpg.ssh.defaultKeyCommand must be set for SSH signing.
+signNotUserCertificate=Certificate with CA key {0} used for the SSH signature is not a user certificate.
+signPublicKeyError=Cannot read public key {0}
+signSeeLog=SSH signature verification failed; see the log for details
+signSignatureError=Could not create the signature
+signStderr=Cannot read stderr
+signTooManyPrivateKeys=Private key file {0} must contain exactly one private key
+signTooManyPublicKeys=Public key file {0} must contain exactly one public key
+signUnknownHashAlgorithm=SSH Signature has an unknown hash algorithm {0}
+signUnknownSignatureAlgorithm=SSH Signature has an unknown signature algorithm {0}
+signWrongNamespace=Key may not be used in namespace "{0}".
unknownProxyProtocol=Ignoring unknown proxy protocol {0} \ No newline at end of file
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/AllowedSigners.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/AllowedSigners.java
new file mode 100644
index 0000000000..80b171f216
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/AllowedSigners.java
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.util.io.ModifiableFileWatcher;
+import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.signing.ssh.VerificationException;
+import org.eclipse.jgit.util.StringUtils;
+import org.eclipse.jgit.util.SystemReader;
+
+/**
+ * Encapsulates the allowed signers handling.
+ */
+final class AllowedSigners extends ModifiableFileWatcher {
+
+ private static final String CERT_AUTHORITY = "cert-authority"; //$NON-NLS-1$
+
+ private static final String NAMESPACES = "namespaces="; //$NON-NLS-1$
+
+ private static final String VALID_AFTER = "valid-after="; //$NON-NLS-1$
+
+ private static final String VALID_BEFORE = "valid-before="; //$NON-NLS-1$
+
+ private static final DateTimeFormatter SSH_DATE_FORMAT = new DateTimeFormatterBuilder()
+ .appendValue(ChronoField.YEAR, 4)
+ .appendValue(ChronoField.MONTH_OF_YEAR, 2)
+ .appendValue(ChronoField.DAY_OF_MONTH, 2)
+ .optionalStart()
+ .appendValue(ChronoField.HOUR_OF_DAY, 2)
+ .appendValue(ChronoField.MINUTE_OF_HOUR, 2)
+ .optionalStart()
+ .appendValue(ChronoField.SECOND_OF_MINUTE, 2)
+ .toFormatter(Locale.ROOT);
+
+ private static final Predicate<AllowedEntry> CERTIFICATES = AllowedEntry::isCA;
+
+ private static final Predicate<AllowedEntry> PLAIN_KEYS = Predicate
+ .not(CERTIFICATES);
+
+ @SuppressWarnings("ArrayRecordComponent")
+ static record AllowedEntry(String[] identities, boolean isCA,
+ String[] namespaces, Instant validAfter, Instant validBefore,
+ String key) {
+ // Empty
+
+ @Override
+ public final boolean equals(Object any) {
+ if (this == any) {
+ return true;
+ }
+ if (any == null || !(any instanceof AllowedEntry)) {
+ return false;
+ }
+ AllowedEntry other = (AllowedEntry) any;
+ return isCA == other.isCA
+ && Arrays.equals(identities, other.identities)
+ && Arrays.equals(namespaces, other.namespaces)
+ && Objects.equals(validAfter, other.validAfter)
+ && Objects.equals(validBefore, other.validBefore)
+ && Objects.equals(key, other.key);
+ }
+
+ @Override
+ public final int hashCode() {
+ int hash = Boolean.hashCode(isCA);
+ hash = hash * 31 + Arrays.hashCode(identities);
+ hash = hash * 31 + Arrays.hashCode(namespaces);
+ return hash * 31 + Objects.hash(validAfter, validBefore, key);
+ }
+ }
+
+ private static record State(Map<String, List<AllowedEntry>> entries) {
+ // Empty
+ }
+
+ private State state;
+
+ public AllowedSigners(Path path) {
+ super(path);
+ state = new State(new HashMap<>());
+ }
+
+ public String isAllowed(PublicKey key, String namespace, String name,
+ Instant time) throws IOException, VerificationException {
+ State currentState = refresh();
+ PublicKey keyToCheck = key;
+ if (key instanceof OpenSshCertificate certificate) {
+ AllowedEntry entry = find(currentState, certificate.getCaPubKey(),
+ namespace, name, time, CERTIFICATES);
+ if (entry != null) {
+ Collection<String> principals = certificate.getPrincipals();
+ if (principals.isEmpty()) {
+ // According to the OpenSSH documentation, a certificate
+ // without principals is valid for anyone.
+ //
+ // See https://man.openbsd.org/ssh-keygen.1#CERTIFICATES .
+ //
+ // However, the same documentation also says that a name
+ // must match both the entry's patterns and be listed in the
+ // certificate's principals.
+ //
+ // See https://man.openbsd.org/ssh-keygen.1#ALLOWED_SIGNERS
+ //
+ // git/OpenSSH considers signatures made by such
+ // certificates untrustworthy.
+ String identities;
+ if (!StringUtils.isEmptyOrNull(name)) {
+ // The name must have matched entry.identities.
+ identities = name;
+ } else {
+ identities = Arrays.stream(entry.identities())
+ .collect(Collectors.joining(",")); //$NON-NLS-1$
+ }
+ throw new VerificationException(false, MessageFormat.format(
+ SshdText.get().signCertificateWithoutPrincipals,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()),
+ identities));
+ }
+ if (!StringUtils.isEmptyOrNull(name)) {
+ if (!principals.contains(name)) {
+ throw new VerificationException(false,
+ MessageFormat.format(SshdText
+ .get().signCertificateNotForName,
+ KeyUtils.getFingerPrint(
+ certificate.getCaPubKey()),
+ name));
+ }
+ return name;
+ }
+ // Filter the principals listed in the certificate by
+ // the patterns defined in the file.
+ Set<String> filtered = new LinkedHashSet<>();
+ List<String> patterns = Arrays.asList(entry.identities());
+ for (String principal : principals) {
+ if (OpenSshConfigFile.patternMatch(patterns, principal)) {
+ filtered.add(principal);
+ }
+ }
+ return filtered.stream().collect(Collectors.joining(",")); //$NON-NLS-1$
+ }
+ // Certificate not found. git/OpenSSH considers this untrustworthy,
+ // even if the certified key itself might be listed.
+ return null;
+ // Alternative: go check for the certified key itself:
+ // keyToCheck = certificate.getCertPubKey();
+ }
+ AllowedEntry entry = find(currentState, keyToCheck, namespace, name,
+ time, PLAIN_KEYS);
+ if (entry != null) {
+ if (!StringUtils.isEmptyOrNull(name)) {
+ // The name must have matched entry.identities.
+ return name;
+ }
+ // No name given, but we consider the key valid: report the
+ // identities.
+ return Arrays.stream(entry.identities())
+ .collect(Collectors.joining(",")); //$NON-NLS-1$
+ }
+ return null;
+ }
+
+ private AllowedEntry find(State current, PublicKey key,
+ String namespace, String name, Instant time,
+ Predicate<AllowedEntry> filter)
+ throws VerificationException {
+ String k = PublicKeyEntry.toString(key);
+ VerificationException v = null;
+ List<AllowedEntry> candidates = current.entries().get(k);
+ if (candidates == null) {
+ return null;
+ }
+ for (AllowedEntry entry : candidates) {
+ if (!filter.test(entry)) {
+ continue;
+ }
+ if (name != null && !OpenSshConfigFile
+ .patternMatch(Arrays.asList(entry.identities()), name)) {
+ continue;
+ }
+ if (entry.namespaces() != null) {
+ if (!OpenSshConfigFile.patternMatch(
+ Arrays.asList(entry.namespaces()),
+ namespace)) {
+ if (v == null) {
+ v = new VerificationException(false,
+ MessageFormat.format(
+ SshdText.get().signWrongNamespace,
+ KeyUtils.getFingerPrint(key),
+ namespace));
+ }
+ continue;
+ }
+ }
+ if (time != null) {
+ if (entry.validAfter() != null
+ && time.isBefore(entry.validAfter())) {
+ if (v == null) {
+ v = new VerificationException(true,
+ MessageFormat.format(
+ SshdText.get().signKeyTooEarly,
+ KeyUtils.getFingerPrint(key)));
+ }
+ continue;
+ } else if (entry.validBefore() != null
+ && time.isAfter(entry.validBefore())) {
+ if (v == null) {
+ v = new VerificationException(true,
+ MessageFormat.format(
+ SshdText.get().signKeyTooEarly,
+ KeyUtils.getFingerPrint(key)));
+ }
+ continue;
+ }
+ }
+ return entry;
+ }
+ if (v != null) {
+ throw v;
+ }
+ return null;
+ }
+
+ private synchronized State refresh() throws IOException {
+ if (checkReloadRequired()) {
+ updateReloadAttributes();
+ try {
+ state = reload(getPath());
+ } catch (NoSuchFileException e) {
+ // File disappeared
+ resetReloadAttributes();
+ state = new State(new HashMap<>());
+ }
+ }
+ return state;
+ }
+
+ private static State reload(Path path) throws IOException {
+ Map<String, List<AllowedEntry>> entries = new HashMap<>();
+ try (BufferedReader r = Files.newBufferedReader(path,
+ StandardCharsets.UTF_8)) {
+ String line;
+ for (int lineNumber = 1;; lineNumber++) {
+ line = r.readLine();
+ if (line == null) {
+ break;
+ }
+ line = line.strip();
+ try {
+ AllowedEntry entry = parseLine(line);
+ if (entry != null) {
+ entries.computeIfAbsent(entry.key(),
+ k -> new ArrayList<>()).add(entry);
+ }
+ } catch (IOException | RuntimeException e) {
+ throw new IOException(MessageFormat.format(
+ SshdText.get().signAllowedSignersFormatError, path,
+ Integer.toString(lineNumber), line), e);
+ }
+ }
+ }
+ return new State(entries);
+ }
+
+ private static boolean matches(String src, String other, int offset) {
+ return src.regionMatches(true, offset, other, 0, other.length());
+ }
+
+ // Things below have package visibility for testing.
+
+ static AllowedEntry parseLine(String line)
+ throws IOException {
+ if (StringUtils.isEmptyOrNull(line) || line.charAt(0) == '#') {
+ return null;
+ }
+ int length = line.length();
+ if ((matches(line, CERT_AUTHORITY, 0)
+ && CERT_AUTHORITY.length() < length
+ && Character.isWhitespace(line.charAt(CERT_AUTHORITY.length())))
+ || matches(line, NAMESPACES, 0)
+ || matches(line, VALID_AFTER, 0)
+ || matches(line, VALID_BEFORE, 0)) {
+ throw new StreamCorruptedException(
+ SshdText.get().signAllowedSignersNoIdentities);
+ }
+ int i = 0;
+ while (i < length && !Character.isWhitespace(line.charAt(i))) {
+ i++;
+ }
+ if (i >= length) {
+ throw new StreamCorruptedException(SshdText.get().signAllowedSignersLineFormat);
+ }
+ String[] identities = line.substring(0, i).split(","); //$NON-NLS-1$
+ if (Arrays.stream(identities).anyMatch(String::isEmpty)) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersEmptyIdentity,
+ line.substring(0, i)));
+ }
+ // Parse the options
+ i++;
+ boolean isCA = false;
+ List<String> namespaces = null;
+ Instant validAfter = null;
+ Instant validBefore = null;
+ while (i < length) {
+ // Skip whitespace
+ if (Character.isSpaceChar(line.charAt(i))) {
+ i++;
+ continue;
+ }
+ if (matches(line, CERT_AUTHORITY, i)) {
+ i += CERT_AUTHORITY.length();
+ isCA = true;
+ if (!Character.isWhitespace(line.charAt(i))) {
+ throw new StreamCorruptedException(SshdText.get().signAllowedSignersCertAuthorityError);
+ }
+ i++;
+ } else if (matches(line, NAMESPACES, i)) {
+ if (namespaces != null) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersMultiple,
+ NAMESPACES));
+ }
+ i += NAMESPACES.length();
+ Dequoted parsed = dequote(line, i);
+ i = parsed.after();
+ String ns = parsed.value();
+ String[] items = ns.split(","); //$NON-NLS-1$
+ namespaces = new ArrayList<>(items.length);
+ for (int j = 0; j < items.length; j++) {
+ String n = items[j].strip();
+ if (!n.isEmpty()) {
+ namespaces.add(n);
+ }
+ }
+ if (namespaces.isEmpty()) {
+ throw new StreamCorruptedException(
+ SshdText.get().signAllowedSignersEmptyNamespaces);
+ }
+ } else if (matches(line, VALID_AFTER, i)) {
+ if (validAfter != null) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersMultiple,
+ VALID_AFTER));
+ }
+ i += VALID_AFTER.length();
+ Dequoted parsed = dequote(line, i);
+ i = parsed.after();
+ validAfter = parseDate(parsed.value());
+ } else if (matches(line, VALID_BEFORE, i)) {
+ if (validBefore != null) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersMultiple,
+ VALID_BEFORE));
+ }
+ i += VALID_BEFORE.length();
+ Dequoted parsed = dequote(line, i);
+ i = parsed.after();
+ validBefore = parseDate(parsed.value());
+ } else {
+ break;
+ }
+ }
+ // Now we should be at the key
+ String key = parsePublicKey(line, i);
+ return new AllowedEntry(identities, isCA,
+ namespaces == null ? null : namespaces.toArray(new String[0]),
+ validAfter, validBefore, key);
+ }
+
+ static String parsePublicKey(String s, int from)
+ throws StreamCorruptedException {
+ int i = from;
+ int length = s.length();
+ while (i < length && Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+ if (i >= length) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersPublicKeyParsing,
+ s.substring(from)));
+ }
+ int start = i;
+ while (i < length && !Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+ if (i >= length) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersPublicKeyParsing,
+ s.substring(start)));
+ }
+ int endOfKeyType = i;
+ i = endOfKeyType + 1;
+ while (i < length && Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+ int startOfKey = i;
+ while (i < length && !Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+ if (i == startOfKey) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersPublicKeyParsing,
+ s.substring(start)));
+ }
+ String keyType = s.substring(start, endOfKeyType);
+ String key = s.substring(startOfKey, i);
+ if (!key.startsWith("AAAA")) { //$NON-NLS-1$
+ // base64 encoded SSH keys always start with four 'A's.
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersPublicKeyParsing,
+ s.substring(start)));
+ }
+ return keyType + ' ' + s.substring(startOfKey, i);
+ }
+
+ static Instant parseDate(String input) {
+ // Allowed formats are YYYYMMDD[Z] or YYYYMMDDHHMM[SS][Z]. If 'Z', it's
+ // UTC, otherwise local time.
+ String timeSpec = input;
+ int length = input.length();
+ if (length < 8) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ SshdText.get().signAllowedSignersInvalidDate, input));
+ }
+ boolean isUTC = false;
+ if (timeSpec.charAt(length - 1) == 'Z') {
+ isUTC = true;
+ timeSpec = timeSpec.substring(0, length - 1);
+ }
+ LocalDateTime time;
+ TemporalAccessor temporalAccessor = SSH_DATE_FORMAT.parseBest(timeSpec,
+ LocalDateTime::from, LocalDate::from);
+ if (temporalAccessor instanceof LocalDateTime) {
+ time = (LocalDateTime) temporalAccessor;
+ } else {
+ time = ((LocalDate) temporalAccessor).atStartOfDay();
+ }
+ if (isUTC) {
+ return time.atOffset(ZoneOffset.UTC).toInstant();
+ }
+ ZoneId tz = SystemReader.getInstance().getTimeZoneId();
+ return time.atZone(tz).toInstant();
+ }
+
+ // OpenSSH uses the backslash *only* to quote the double-quote.
+ static Dequoted dequote(String line, int from) {
+ int length = line.length();
+ int i = from;
+ if (line.charAt(i) == '"') {
+ boolean quoted = false;
+ i++;
+ StringBuilder b = new StringBuilder();
+ while (i < length) {
+ char ch = line.charAt(i);
+ if (ch == '"') {
+ if (quoted) {
+ b.append(ch);
+ quoted = false;
+ } else {
+ break;
+ }
+ } else if (ch == '\\') {
+ quoted = true;
+ } else {
+ if (quoted) {
+ b.append('\\');
+ }
+ b.append(ch);
+ quoted = false;
+ }
+ i++;
+ }
+ if (i >= length) {
+ throw new IllegalArgumentException(
+ SshdText.get().signAllowedSignersUnterminatedQuote);
+ }
+ return new Dequoted(b.toString(), i + 1);
+ }
+ while (i < length && !Character.isWhitespace(line.charAt(i))) {
+ i++;
+ }
+ return new Dequoted(line.substring(from, i), i);
+ }
+
+ static record Dequoted(String value, int after) {
+ // Empty
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrl.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrl.java
new file mode 100644
index 0000000000..6b19eb3295
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrl.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.StringUtils;
+
+/**
+ * An implementation of OpenSSH binary format key revocation lists (KRLs).
+ *
+ * @see <a href=
+ * "https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.krl">PROTOCOL.krl</a>
+ */
+class OpenSshBinaryKrl {
+
+ /**
+ * The "magic" bytes at the start of an OpenSSH binary KRL.
+ */
+ static final byte[] MAGIC = { 'S', 'S', 'H', 'K', 'R', 'L', '\n', 0 };
+
+ private static final int FORMAT_VERSION = 1;
+
+ private static final int SECTION_CERTIFICATES = 1;
+
+ private static final int SECTION_KEY = 2;
+
+ private static final int SECTION_SHA1 = 3;
+
+ private static final int SECTION_SIGNATURE = 4; // Skipped
+
+ private static final int SECTION_SHA256 = 5;
+
+ private static final int SECTION_EXTENSION = 255; // Skipped
+
+ // Certificates
+
+ private static final int CERT_SERIAL_LIST = 0x20;
+
+ private static final int CERT_SERIAL_RANGES = 0x21;
+
+ private static final int CERT_SERIAL_BITS = 0x22;
+
+ private static final int CERT_KEY_IDS = 0x23;
+
+ private static final int CERT_EXTENSIONS = 0x39; // Skipped
+
+ private final Map<Blob, CertificateRevocation> certificates = new HashMap<>();
+
+ private static class CertificateRevocation {
+
+ final SerialRangeSet ranges = new SerialRangeSet();
+
+ final Set<String> keyIds = new HashSet<>();
+ }
+
+ // Plain keys
+
+ /**
+ * A byte array that can be used as a key in a {@link Map} or {@link Set}.
+ * {@link #equals(Object)} and {@link #hashCode()} are based on the content.
+ *
+ * @param blob
+ * the array to wrap
+ */
+ @SuppressWarnings("ArrayRecordComponent")
+ private static record Blob(byte[] blob) {
+
+ @Override
+ public final boolean equals(Object any) {
+ if (this == any) {
+ return true;
+ }
+ if (any == null || !(any instanceof Blob)) {
+ return false;
+ }
+ Blob other = (Blob) any;
+ return Arrays.equals(blob, other.blob);
+ }
+
+ @Override
+ public final int hashCode() {
+ return Arrays.hashCode(blob);
+ }
+ }
+
+ private final Set<Blob> blobs = new HashSet<>();
+
+ private final Set<Blob> sha1 = new HashSet<>();
+
+ private final Set<Blob> sha256 = new HashSet<>();
+
+ private OpenSshBinaryKrl() {
+ // No public instantiation, use load(InputStream, boolean) instead.
+ }
+
+ /**
+ * Tells whether the given key has been revoked.
+ *
+ * @param key
+ * {@link PublicKey} to check
+ * @return {@code true} if the key was revoked, {@code false} otherwise
+ */
+ boolean isRevoked(PublicKey key) {
+ if (key instanceof OpenSshCertificate certificate) {
+ if (certificates.isEmpty()) {
+ return false;
+ }
+ // These apply to all certificates
+ if (isRevoked(certificate, certificates.get(null))) {
+ return true;
+ }
+ if (isRevoked(certificate,
+ certificates.get(blob(certificate.getCaPubKey())))) {
+ return true;
+ }
+ // Keys themselves are checked in OpenSshKrl.
+ return false;
+ }
+ if (!blobs.isEmpty() && blobs.contains(blob(key))) {
+ return true;
+ }
+ if (!sha256.isEmpty() && sha256.contains(hash("SHA256", key))) { //$NON-NLS-1$
+ return true;
+ }
+ if (!sha1.isEmpty() && sha1.contains(hash("SHA1", key))) { //$NON-NLS-1$
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isRevoked(OpenSshCertificate certificate,
+ CertificateRevocation revocations) {
+ if (revocations == null) {
+ return false;
+ }
+ String id = certificate.getId();
+ if (!StringUtils.isEmptyOrNull(id) && revocations.keyIds.contains(id)) {
+ return true;
+ }
+ long serial = certificate.getSerial();
+ if (serial != 0 && revocations.ranges.contains(serial)) {
+ return true;
+ }
+ return false;
+ }
+
+ private Blob blob(PublicKey key) {
+ ByteArrayBuffer buf = new ByteArrayBuffer();
+ buf.putRawPublicKey(key);
+ return new Blob(buf.getCompactData());
+ }
+
+ private Blob hash(String algorithm, PublicKey key) {
+ ByteArrayBuffer buf = new ByteArrayBuffer();
+ buf.putRawPublicKey(key);
+ try {
+ return new Blob(MessageDigest.getInstance(algorithm)
+ .digest(buf.getCompactData()));
+ } catch (NoSuchAlgorithmException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Loads a binary KRL from the given stream.
+ *
+ * @param in
+ * {@link InputStream} to read from
+ * @param magicSkipped
+ * whether the {@link #MAGIC} bytes at the beginning have already
+ * been skipped
+ * @return a new {@link OpenSshBinaryKrl}.
+ * @throws IOException
+ * if the stream cannot be read as an OpenSSH binary KRL
+ */
+ @NonNull
+ static OpenSshBinaryKrl load(InputStream in, boolean magicSkipped)
+ throws IOException {
+ if (!magicSkipped) {
+ byte[] magic = new byte[MAGIC.length];
+ IO.readFully(in, magic);
+ if (!Arrays.equals(magic, MAGIC)) {
+ throw new StreamCorruptedException(
+ SshdText.get().signKrlInvalidMagic);
+ }
+ }
+ skipHeader(in);
+ return load(in);
+ }
+
+ private static long getUInt(InputStream in) throws IOException {
+ byte[] buf = new byte[Integer.BYTES];
+ IO.readFully(in, buf);
+ return BufferUtils.getUInt(buf);
+ }
+
+ private static long getLong(InputStream in) throws IOException {
+ byte[] buf = new byte[Long.BYTES];
+ IO.readFully(in, buf);
+ return BufferUtils.getLong(buf, 0, Long.BYTES);
+ }
+
+ private static void skipHeader(InputStream in) throws IOException {
+ long version = getUInt(in);
+ if (version != FORMAT_VERSION) {
+ throw new StreamCorruptedException(
+ MessageFormat.format(SshdText.get().signKrlInvalidVersion,
+ Long.valueOf(version)));
+ }
+ // krl_version, generated_date, flags (none defined in version 1)
+ in.skip(24);
+ in.skip(getUInt(in)); // reserved
+ in.skip(getUInt(in)); // comment
+ }
+
+ private static OpenSshBinaryKrl load(InputStream in) throws IOException {
+ OpenSshBinaryKrl krl = new OpenSshBinaryKrl();
+ for (;;) {
+ int sectionType = in.read();
+ if (sectionType < 0) {
+ break; // EOF
+ }
+ switch (sectionType) {
+ case SECTION_CERTIFICATES:
+ readCertificates(krl.certificates, in, getUInt(in));
+ break;
+ case SECTION_KEY:
+ readBlobs("explicit_keys", krl.blobs, in, getUInt(in), 0); //$NON-NLS-1$
+ break;
+ case SECTION_SHA1:
+ readBlobs("fingerprint_sha1", krl.sha1, in, getUInt(in), 20); //$NON-NLS-1$
+ break;
+ case SECTION_SIGNATURE:
+ // Unsupported as of OpenSSH 9.4. It even refuses to load such
+ // KRLs. Just skip it.
+ in.skip(getUInt(in));
+ break;
+ case SECTION_SHA256:
+ readBlobs("fingerprint_sha256", krl.sha256, in, getUInt(in), //$NON-NLS-1$
+ 32);
+ break;
+ case SECTION_EXTENSION:
+ // No extensions are defined for version 1 KRLs.
+ in.skip(getUInt(in));
+ break;
+ default:
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlUnknownSection,
+ Integer.valueOf(sectionType)));
+ }
+ }
+ return krl;
+ }
+
+ private static void readBlobs(String sectionName, Set<Blob> blobs,
+ InputStream in, long sectionLength, long expectedBlobLength)
+ throws IOException {
+ while (sectionLength >= Integer.BYTES) {
+ // Read blobs.
+ long blobLength = getUInt(in);
+ sectionLength -= Integer.BYTES;
+ if (blobLength > sectionLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlBlobLengthInvalid, sectionName,
+ Long.valueOf(blobLength)));
+ }
+ if (expectedBlobLength != 0 && blobLength != expectedBlobLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlBlobLengthInvalidExpected,
+ sectionName, Long.valueOf(blobLength),
+ Long.valueOf(expectedBlobLength)));
+ }
+ byte[] blob = new byte[(int) blobLength];
+ IO.readFully(in, blob);
+ sectionLength -= blobLength;
+ blobs.add(new Blob(blob));
+ }
+ if (sectionLength != 0) {
+ throw new StreamCorruptedException(
+ MessageFormat.format(SshdText.get().signKrlBlobLeftover,
+ sectionName, Long.valueOf(sectionLength)));
+ }
+ }
+
+ private static void readCertificates(Map<Blob, CertificateRevocation> certs,
+ InputStream in, long sectionLength) throws IOException {
+ long keyLength = getUInt(in);
+ sectionLength -= Integer.BYTES;
+ if (keyLength > sectionLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCaKeyLengthInvalid,
+ Long.valueOf(keyLength)));
+ }
+ Blob key = null;
+ if (keyLength > 0) {
+ byte[] blob = new byte[(int) keyLength];
+ IO.readFully(in, blob);
+ key = new Blob(blob);
+ sectionLength -= keyLength;
+ }
+ CertificateRevocation rev = certs.computeIfAbsent(key,
+ k -> new CertificateRevocation());
+ long reservedLength = getUInt(in);
+ sectionLength -= Integer.BYTES;
+ if (reservedLength > sectionLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCaKeyLengthInvalid,
+ Long.valueOf(reservedLength)));
+ }
+ in.skip(reservedLength);
+ sectionLength -= reservedLength;
+ if (sectionLength == 0) {
+ throw new StreamCorruptedException(
+ SshdText.get().signKrlNoCertificateSubsection);
+ }
+ while (sectionLength > 0) {
+ int subSection = in.read();
+ if (subSection < 0) {
+ throw new EOFException();
+ }
+ sectionLength--;
+ if (sectionLength < Integer.BYTES) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCertificateLeftover,
+ Long.valueOf(sectionLength)));
+ }
+ long subLength = getUInt(in);
+ sectionLength -= Integer.BYTES;
+ if (subLength > sectionLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCertificateSubsectionLength,
+ Long.valueOf(subLength)));
+ }
+ if (subLength > 0) {
+ switch (subSection) {
+ case CERT_SERIAL_LIST:
+ readSerials(rev.ranges, in, subLength, false);
+ break;
+ case CERT_SERIAL_RANGES:
+ readSerials(rev.ranges, in, subLength, true);
+ break;
+ case CERT_SERIAL_BITS:
+ readSerialBitSet(rev.ranges, in, subLength);
+ break;
+ case CERT_KEY_IDS:
+ readIds(rev.keyIds, in, subLength);
+ break;
+ case CERT_EXTENSIONS:
+ in.skip(subLength);
+ break;
+ default:
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlUnknownSubsection,
+ Long.valueOf(subSection)));
+ }
+ }
+ sectionLength -= subLength;
+ }
+ }
+
+ private static void readSerials(SerialRangeSet set, InputStream in,
+ long length, boolean ranges) throws IOException {
+ while (length >= Long.BYTES) {
+ long a = getLong(in);
+ length -= Long.BYTES;
+ if (a == 0) {
+ throw new StreamCorruptedException(
+ SshdText.get().signKrlSerialZero);
+ }
+ if (!ranges) {
+ set.add(a);
+ continue;
+ }
+ if (length < Long.BYTES) {
+ throw new StreamCorruptedException(
+ MessageFormat.format(SshdText.get().signKrlShortRange,
+ Long.valueOf(length)));
+ }
+ long b = getLong(in);
+ length -= Long.BYTES;
+ if (Long.compareUnsigned(a, b) > 0) {
+ throw new StreamCorruptedException(
+ SshdText.get().signKrlEmptyRange);
+ }
+ set.add(a, b);
+ }
+ if (length != 0) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCertificateSubsectionLeftover,
+ Long.valueOf(length)));
+ }
+ }
+
+ private static void readSerialBitSet(SerialRangeSet set, InputStream in,
+ long subLength) throws IOException {
+ while (subLength > 0) {
+ if (subLength < Long.BYTES) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCertificateSubsectionLeftover,
+ Long.valueOf(subLength)));
+ }
+ long base = getLong(in);
+ subLength -= Long.BYTES;
+ if (subLength < Integer.BYTES) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCertificateSubsectionLeftover,
+ Long.valueOf(subLength)));
+ }
+ long setLength = getUInt(in);
+ subLength -= Integer.BYTES;
+ if (setLength == 0 || setLength > subLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlInvalidBitSetLength,
+ Long.valueOf(setLength)));
+ }
+ // Now process the bits. Note that the mpint is stored MSB first.
+ //
+ // We set individual serial numbers (one for each set bit) and let
+ // the SerialRangeSet take care of coalescing for successive runs
+ // of set bits.
+ int n = (int) setLength;
+ for (int i = n - 1; i >= 0; i--) {
+ int b = in.read();
+ if (b < 0) {
+ throw new EOFException();
+ } else if (b == 0) {
+ // Stored as an mpint: may have leading zero bytes (actually
+ // at most one; if the high bit of the first byte is set).
+ continue;
+ }
+ for (int bit = 0,
+ mask = 1; bit < Byte.SIZE; bit++, mask <<= 1) {
+ if ((b & mask) != 0) {
+ set.add(base + (i * Byte.SIZE) + bit);
+ }
+ }
+ }
+ subLength -= setLength;
+ }
+ }
+
+ private static void readIds(Set<String> ids, InputStream in, long subLength)
+ throws IOException {
+ while (subLength >= Integer.BYTES) {
+ long length = getUInt(in);
+ subLength -= Integer.BYTES;
+ if (length > subLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlInvalidKeyIdLength,
+ Long.valueOf(length)));
+ }
+ byte[] bytes = new byte[(int) length];
+ IO.readFully(in, bytes);
+ ids.add(new String(bytes, StandardCharsets.UTF_8));
+ subLength -= length;
+ }
+ if (subLength != 0) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCertificateSubsectionLeftover,
+ Long.valueOf(subLength)));
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshKrl.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshKrl.java
new file mode 100644
index 0000000000..7993def90c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshKrl.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.util.io.ModifiableFileWatcher;
+import org.eclipse.jgit.util.IO;
+
+/**
+ * An implementation of an OpenSSH key revocation list (KRL), either a binary
+ * KRL or a simple list of public keys.
+ */
+class OpenSshKrl extends ModifiableFileWatcher {
+
+ private static record State(Set<String> keys, OpenSshBinaryKrl krl) {
+ // Empty
+ }
+
+ private State state;
+
+ public OpenSshKrl(Path path) {
+ super(path);
+ state = new State(Set.of(), null);
+ }
+
+ public boolean isRevoked(PublicKey key) throws IOException {
+ State current = refresh();
+ return isRevoked(current, key);
+ }
+
+ private boolean isRevoked(State current, PublicKey key) {
+ if (key instanceof OpenSshCertificate cert) {
+ OpenSshBinaryKrl krl = current.krl();
+ if (krl != null && krl.isRevoked(cert)) {
+ return true;
+ }
+ if (isRevoked(current, cert.getCaPubKey())
+ || isRevoked(current, cert.getCertPubKey())) {
+ return true;
+ }
+ return false;
+ }
+ OpenSshBinaryKrl krl = current.krl();
+ if (krl != null) {
+ return krl.isRevoked(key);
+ }
+ return current.keys().contains(PublicKeyEntry.toString(key));
+ }
+
+ private synchronized State refresh() throws IOException {
+ if (checkReloadRequired()) {
+ updateReloadAttributes();
+ try {
+ state = reload(getPath());
+ } catch (NoSuchFileException e) {
+ // File disappeared
+ resetReloadAttributes();
+ state = new State(Set.of(), null);
+ }
+ }
+ return state;
+ }
+
+ private static State reload(Path path) throws IOException {
+ try (BufferedInputStream in = new BufferedInputStream(
+ Files.newInputStream(path))) {
+ byte[] magic = new byte[OpenSshBinaryKrl.MAGIC.length];
+ in.mark(magic.length);
+ IO.readFully(in, magic);
+ if (Arrays.equals(magic, OpenSshBinaryKrl.MAGIC)) {
+ return new State(null, OpenSshBinaryKrl.load(in, true));
+ }
+ // Otherwise try reading it textually
+ in.reset();
+ return loadTextKrl(in);
+ }
+ }
+
+ private static State loadTextKrl(InputStream in) throws IOException {
+ Set<String> keys = new HashSet<>();
+ try (BufferedReader r = new BufferedReader(
+ new InputStreamReader(in, StandardCharsets.UTF_8))) {
+ String line;
+ for (;;) {
+ line = r.readLine();
+ if (line == null) {
+ break;
+ }
+ line = line.strip();
+ if (line.isEmpty() || line.charAt(0) == '#') {
+ continue;
+ }
+ keys.add(AllowedSigners.parsePublicKey(line, 0));
+ }
+ }
+ return new State(keys, null);
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshSigningKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshSigningKeyDatabase.java
new file mode 100644
index 0000000000..aa26886839
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshSigningKeyDatabase.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.PublicKey;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.signing.ssh.CachingSigningKeyDatabase;
+import org.eclipse.jgit.signing.ssh.VerificationException;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.StringUtils;
+
+/**
+ * A {@link CachingSigningKeyDatabase} using the OpenSSH allowed signers file
+ * and the OpenSSH key revocation list.
+ */
+public class OpenSshSigningKeyDatabase implements CachingSigningKeyDatabase {
+
+ // Keep caches of allowed signers and KRLs. Cache by canonical path.
+
+ private static final int DEFAULT_CACHE_SIZE = 5;
+
+ private AtomicInteger cacheSize = new AtomicInteger(DEFAULT_CACHE_SIZE);
+
+ private class LRU<K, V> extends LinkedHashMap<K, V> {
+
+ private static final long serialVersionUID = 1L;
+
+ LRU() {
+ super(DEFAULT_CACHE_SIZE, 0.75f, true);
+ }
+
+ @Override
+ protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
+ return size() > cacheSize.get();
+ }
+ }
+
+ private final HashMap<Path, AllowedSigners> allowedSigners = new LRU<>();
+
+ private final HashMap<Path, OpenSshKrl> revocations = new LRU<>();
+
+ @Override
+ public boolean isRevoked(Repository repository, GpgConfig config,
+ PublicKey key) throws IOException {
+ String fileName = config.getSshRevocationFile();
+ if (StringUtils.isEmptyOrNull(fileName)) {
+ return false;
+ }
+ File file = getFile(repository, fileName);
+ OpenSshKrl revocationList;
+ synchronized (revocations) {
+ revocationList = revocations.computeIfAbsent(file.toPath(),
+ OpenSshKrl::new);
+ }
+ return revocationList.isRevoked(key);
+ }
+
+ @Override
+ public String isAllowed(Repository repository, GpgConfig config,
+ PublicKey key, String namespace, PersonIdent ident)
+ throws IOException, VerificationException {
+ String fileName = config.getSshAllowedSignersFile();
+ if (StringUtils.isEmptyOrNull(fileName)) {
+ // No file configured. Git would error out.
+ return null;
+ }
+ File file = getFile(repository, fileName);
+ AllowedSigners allowed;
+ synchronized (allowedSigners) {
+ allowed = allowedSigners.computeIfAbsent(file.toPath(),
+ AllowedSigners::new);
+ }
+ Instant gitTime = null;
+ if (ident != null) {
+ gitTime = ident.getWhenAsInstant();
+ }
+ return allowed.isAllowed(key, namespace, null, gitTime);
+ }
+
+ private File getFile(@NonNull Repository repository, String fileName)
+ throws IOException {
+ File file;
+ if (fileName.startsWith("~/") //$NON-NLS-1$
+ || fileName.startsWith('~' + File.separator)) {
+ file = FS.DETECTED.resolve(FS.DETECTED.userHome(),
+ fileName.substring(2));
+ } else {
+ file = new File(fileName);
+ if (!file.isAbsolute()) {
+ file = new File(repository.getWorkTree(), fileName);
+ }
+ }
+ return file.getCanonicalFile();
+ }
+
+ @Override
+ public int getCacheSize() {
+ return cacheSize.get();
+ }
+
+ @Override
+ public void setCacheSize(int size) {
+ if (size > 0) {
+ cacheSize.set(size);
+ pruneCache(size);
+ }
+ }
+
+ private void pruneCache(int size) {
+ prune(allowedSigners, size);
+ prune(revocations, size);
+ }
+
+ private void prune(HashMap<?, ?> map, int size) {
+ synchronized (map) {
+ if (map.size() <= size) {
+ return;
+ }
+ Iterator<?> iter = map.entrySet().iterator();
+ int i = 0;
+ while (iter.hasNext() && i < size) {
+ iter.next();
+ i++;
+ }
+ while (iter.hasNext()) {
+ iter.next();
+ iter.remove();
+ }
+ }
+ }
+
+ @Override
+ public void clearCache() {
+ synchronized (allowedSigners) {
+ allowedSigners.clear();
+ }
+ synchronized (revocations) {
+ revocations.clear();
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SerialRangeSet.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SerialRangeSet.java
new file mode 100644
index 0000000000..f4eb884239
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SerialRangeSet.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.util.TreeMap;
+
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+
+/**
+ * Encapsulates the storage for revoked certificate serial numbers.
+ */
+class SerialRangeSet {
+
+ /**
+ * A range of certificate serial numbers [from..to], i.e., with both range
+ * limits included.
+ */
+ private interface SerialRange {
+
+ long from();
+
+ long to();
+ }
+
+ private static record Singleton(long from) implements SerialRange {
+
+ @Override
+ public long to() {
+ return from;
+ }
+ }
+
+ private static record Range(long from, long to) implements SerialRange {
+
+ public Range(long from, long to) {
+ if (Long.compareUnsigned(from, to) > 0) {
+ throw new IllegalArgumentException(
+ SshdText.get().signKrlEmptyRange);
+ }
+ this.from = from;
+ this.to = to;
+ }
+ }
+
+ // We use the same data structure as OpenSSH; basically a TreeSet of mutable
+ // SerialRanges. To get "mutability", the set is implemented as a TreeMap
+ // with the same elements as keys and values.
+ //
+ // get(x) will return null if none of the serial numbers in the range x is
+ // in the set, and some range (partially) overlapping with x otherwise.
+ //
+ // containsKey(x) will return true if there is any (partially) overlapping
+ // range in the TreeMap.
+ private final TreeMap<SerialRange, SerialRange> ranges = new TreeMap<>(
+ SerialRangeSet::compare);
+
+ private static int compare(SerialRange a, SerialRange b) {
+ // Return == if they overlap
+ if (Long.compareUnsigned(a.to(), b.from()) >= 0
+ && Long.compareUnsigned(a.from(), b.to()) <= 0) {
+ return 0;
+ }
+ return Long.compareUnsigned(a.from(), b.from());
+ }
+
+ void add(long serial) {
+ add(ranges, new Singleton(serial));
+ }
+
+ void add(long from, long to) {
+ add(ranges, new Range(from, to));
+ }
+
+ boolean contains(long serial) {
+ return ranges.containsKey(new Singleton(serial));
+ }
+
+ int size() {
+ return ranges.size();
+ }
+
+ boolean isEmpty() {
+ return ranges.isEmpty();
+ }
+
+ private static void add(TreeMap<SerialRange, SerialRange> ranges,
+ SerialRange newRange) {
+ for (;;) {
+ SerialRange existing = ranges.get(newRange);
+ if (existing == null) {
+ break;
+ }
+ if (Long.compareUnsigned(existing.from(), newRange.from()) <= 0
+ && Long.compareUnsigned(existing.to(),
+ newRange.to()) >= 0) {
+ // newRange completely contained in existing
+ return;
+ }
+ ranges.remove(existing);
+ long newFrom = newRange.from();
+ if (Long.compareUnsigned(existing.from(), newFrom) < 0) {
+ newFrom = existing.from();
+ }
+ long newTo = newRange.to();
+ if (Long.compareUnsigned(existing.to(), newTo) > 0) {
+ newTo = existing.to();
+ }
+ newRange = new Range(newFrom, newTo);
+ }
+ // No overlapping range exists: check for coalescing with the
+ // previous/next range
+ SerialRange prev = ranges.floorKey(newRange);
+ if (prev != null && newRange.from() - prev.to() == 1) {
+ ranges.remove(prev);
+ newRange = new Range(prev.from(), newRange.to());
+ }
+ SerialRange next = ranges.ceilingKey(newRange);
+ if (next != null && next.from() - newRange.to() == 1) {
+ ranges.remove(next);
+ newRange = new Range(newRange.from(), next.to());
+ }
+ ranges.put(newRange, newRange);
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SigningDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SigningDatabase.java
new file mode 100644
index 0000000000..e2e1a36840
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SigningDatabase.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import org.eclipse.jgit.signing.ssh.CachingSigningKeyDatabase;
+import org.eclipse.jgit.signing.ssh.SigningKeyDatabase;
+
+/**
+ * A global {@link SigningKeyDatabase} instance.
+ */
+public final class SigningDatabase {
+
+ private static SigningKeyDatabase INSTANCE = new OpenSshSigningKeyDatabase();
+
+ private SigningDatabase() {
+ // No instantiation
+ }
+
+ /**
+ * Obtains the current instance.
+ *
+ * @return the global {@link SigningKeyDatabase}
+ */
+ public static synchronized SigningKeyDatabase getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Sets the global {@link SigningKeyDatabase}.
+ *
+ * @param database
+ * to set; if {@code null} a default database using the OpenSSH
+ * allowed signers file and the OpenSSH revocation list mechanism
+ * is used.
+ * @return the previously set {@link SigningKeyDatabase}
+ */
+ public static synchronized SigningKeyDatabase setInstance(
+ SigningKeyDatabase database) {
+ SigningKeyDatabase previous = INSTANCE;
+ if (database != INSTANCE) {
+ if (INSTANCE instanceof CachingSigningKeyDatabase caching) {
+ caching.clearCache();
+ }
+ if (database == null) {
+ INSTANCE = new OpenSshSigningKeyDatabase();
+ } else {
+ INSTANCE = database;
+ }
+ }
+ return previous;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtils.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtils.java
new file mode 100644
index 0000000000..040c6d4368
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtils.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.time.Instant;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+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.buffer.ByteArrayBuffer;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility methods for working with OpenSSH certificates.
+ */
+final class SshCertificateUtils {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(SshCertificateUtils.class);
+
+ /**
+ * Verifies a certificate: checks that it is a user certificate and has a
+ * valid signature, and if a time is given, that the certificate is valid at
+ * that time.
+ *
+ * @param certificate
+ * {@link OpenSshCertificate} to verify
+ * @param signatureTime
+ * {@link Instant} to check whether the certificate is valid at
+ * that time; maybe {@code null}, in which case the valid-time
+ * check is skipped.
+ * @return {@code null} if the certificate is valid; otherwise a descriptive
+ * message
+ */
+ static String verify(OpenSshCertificate certificate,
+ Instant signatureTime) {
+ if (!OpenSshCertificate.Type.USER.equals(certificate.getType())) {
+ return MessageFormat.format(SshdText.get().signNotUserCertificate,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()));
+ }
+ String message = verifySignature(certificate);
+ if (message == null && signatureTime != null) {
+ message = checkExpiration(certificate, signatureTime);
+ }
+ return message;
+ }
+
+ /**
+ * Verifies the signature on a certificate.
+ *
+ * @param certificate
+ * {@link OpenSshCertificate} to verify
+ * @return {@code null} if the signature is valid; otherwise a descriptive
+ * message
+ */
+ static String verifySignature(OpenSshCertificate certificate) {
+ // Verify the signature on the certificate.
+ //
+ // Note that OpenSSH certificates do not support chaining.
+ //
+ // ssh-keygen refuses to create a certificate for a certificate, so the
+ // certified key cannot be another OpenSshCertificate. Additionally,
+ // when creating a certificate ssh-keygen loads the CA private key to
+ // make the signature and reconstructs the public key that it stores in
+ // the certificate from that, so the CA public key also cannot be an
+ // OpenSshCertificate.
+ PublicKey caKey = certificate.getCaPubKey();
+ PublicKey certifiedKey = certificate.getCertPubKey();
+ if (caKey == null
+ || caKey instanceof OpenSshCertificate
+ || certifiedKey == null
+ || certifiedKey instanceof OpenSshCertificate) {
+ return SshdText.get().signCertificateInvalid;
+ }
+ // Verify that key type and algorithm match
+ String keyType = KeyUtils.getKeyType(caKey);
+ String certAlgorithm = certificate.getSignatureAlgorithm();
+ if (!KeyUtils.getCanonicalKeyType(keyType)
+ .equals(KeyUtils.getCanonicalKeyType(certAlgorithm))) {
+ return MessageFormat.format(
+ SshdText.get().signCertAlgorithmMismatch, keyType,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()),
+ certAlgorithm);
+ }
+ BuiltinSignatures factory = BuiltinSignatures
+ .fromFactoryName(certAlgorithm);
+ if (factory == null || !factory.isSupported()) {
+ return MessageFormat.format(SshdText.get().signCertAlgorithmUnknown,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()),
+ certAlgorithm);
+ }
+ Signature signer = factory.create();
+ try {
+ signer.initVerifier(null, caKey);
+ signer.update(null, getBlob(certificate));
+ if (signer.verify(null, certificate.getRawSignature())) {
+ return null;
+ }
+ } catch (Exception e) {
+ LOG.warn("{}", SshdText.get().signLogFailure, e); //$NON-NLS-1$
+ return SshdText.get().signSeeLog;
+ }
+ return MessageFormat.format(SshdText.get().signCertificateInvalid,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()));
+ }
+
+ private static byte[] getBlob(OpenSshCertificate certificate) {
+ // Theoretically, this should be just certificate.getMessage(). But
+ // Apache MINA sshd has a bug and may return additional bytes if the
+ // certificate is not the first thing in the buffer it was read from.
+ // As a work-around, re-create the signed blob from scratch.
+ //
+ // This may be replaced by return certificate.getMessage() once the
+ // upstream bug is fixed.
+ //
+ // See https://github.com/apache/mina-sshd/issues/618
+ Buffer tmp = new ByteArrayBuffer();
+ tmp.putString(certificate.getKeyType());
+ tmp.putBytes(certificate.getNonce());
+ tmp.putRawPublicKeyBytes(certificate.getCertPubKey());
+ tmp.putLong(certificate.getSerial());
+ tmp.putInt(certificate.getType().getCode());
+ tmp.putString(certificate.getId());
+ Buffer list = new ByteArrayBuffer();
+ list.putStringList(certificate.getPrincipals(), false);
+ tmp.putBytes(list.getCompactData());
+ tmp.putLong(certificate.getValidAfter());
+ tmp.putLong(certificate.getValidBefore());
+ tmp.putCertificateOptions(certificate.getCriticalOptions());
+ tmp.putCertificateOptions(certificate.getExtensions());
+ tmp.putString(certificate.getReserved());
+ Buffer inner = new ByteArrayBuffer();
+ inner.putRawPublicKey(certificate.getCaPubKey());
+ tmp.putBytes(inner.getCompactData());
+ return tmp.getCompactData();
+ }
+
+ /**
+ * Checks whether a certificate is valid at a given time.
+ *
+ * @param certificate
+ * {@link OpenSshCertificate} to check
+ * @param signatureTime
+ * {@link Instant} to check
+ * @return {@code null} if the certificate is valid at the given instant;
+ * otherwise a descriptive message
+ */
+ static String checkExpiration(OpenSshCertificate certificate,
+ @NonNull Instant signatureTime) {
+ long instant = signatureTime.getEpochSecond();
+ if (Long.compareUnsigned(instant, certificate.getValidAfter()) < 0) {
+ return MessageFormat.format(SshdText.get().signCertificateTooEarly,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()));
+ } else if (Long.compareUnsigned(instant,
+ certificate.getValidBefore()) > 0) {
+ return MessageFormat.format(SshdText.get().signCertificateExpired,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()));
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureConstants.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureConstants.java
new file mode 100644
index 0000000000..bc72196a22
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureConstants.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.jgit.lib.Constants;
+
+/**
+ * Defines common constants for SSH signatures.
+ */
+final class SshSignatureConstants {
+
+ private static final String SIGNATURE_END = "-----END SSH SIGNATURE-----"; //$NON-NLS-1$
+
+ static final byte[] MAGIC = { 'S', 'S', 'H', 'S', 'I', 'G' };
+
+ static final int VERSION = 1;
+
+ static final String NAMESPACE = "git"; //$NON-NLS-1$
+
+ static final byte[] ARMOR_HEAD = Constants.SSH_SIGNATURE_PREFIX
+ .getBytes(StandardCharsets.US_ASCII);
+
+ static final byte[] ARMOR_END = SIGNATURE_END
+ .getBytes(StandardCharsets.US_ASCII);
+
+ private SshSignatureConstants() {
+ // No instantiation
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifier.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifier.java
new file mode 100644
index 0000000000..76be340bc7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifier.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.time.Instant;
+import java.util.Date;
+import java.util.Locale;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+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.buffer.ByteArrayBuffer;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SignatureVerifier;
+import org.eclipse.jgit.signing.ssh.CachingSigningKeyDatabase;
+import org.eclipse.jgit.signing.ssh.SigningKeyDatabase;
+import org.eclipse.jgit.signing.ssh.VerificationException;
+import org.eclipse.jgit.util.Base64;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link SignatureVerifier} for SSH signatures.
+ */
+public class SshSignatureVerifier implements SignatureVerifier {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(SshSignatureVerifier.class);
+
+ private static final byte[] OBJECT = { 'o', 'b', 'j', 'e', 'c', 't', ' ' };
+
+ private static final byte[] TREE = { 't', 'r', 'e', 'e', ' ' };
+
+ private static final byte[] TYPE = { 't', 'y', 'p', 'e', ' ' };
+
+ @Override
+ public String getName() {
+ return "ssh"; //$NON-NLS-1$
+ }
+
+ @Override
+ public SignatureVerification verify(Repository repository, GpgConfig config,
+ byte[] data, byte[] signatureData) throws IOException {
+ // This is a bit stupid. SSH signatures do not store a signer, nor a
+ // time the signature was created. So we must use the committer's or
+ // tagger's PersonIdent, but here we have neither. But... if we see
+ // that the data is a commit or tag, then we can parse the PersonIdent
+ // from the data.
+ //
+ // Note: we cannot assume that absent a principal recorded in the
+ // allowedSignersFile or on a certificate that the key used to sign the
+ // commit belonged to the committer.
+ PersonIdent gitIdentity = getGitIdentity(data);
+ Date signatureDate = null;
+ Instant signatureInstant = null;
+ if (gitIdentity != null) {
+ signatureDate = gitIdentity.getWhen();
+ signatureInstant = gitIdentity.getWhenAsInstant();
+ }
+
+ TrustLevel trust = TrustLevel.NEVER;
+ byte[] decodedSignature;
+ try {
+ decodedSignature = dearmor(signatureData);
+ } catch (IllegalArgumentException e) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ null, null, false, false, trust,
+ MessageFormat.format(SshdText.get().signInvalidSignature,
+ e.getLocalizedMessage()));
+ }
+ int start = RawParseUtils.match(decodedSignature, 0,
+ SshSignatureConstants.MAGIC);
+ if (start < 0) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ null, null, false, false, trust,
+ SshdText.get().signInvalidMagic);
+ }
+ ByteArrayBuffer signature = new ByteArrayBuffer(decodedSignature, start,
+ decodedSignature.length - start);
+
+ long version = signature.getUInt();
+ if (version != SshSignatureConstants.VERSION) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ null, null, false, false, trust,
+ MessageFormat.format(SshdText.get().signInvalidVersion,
+ Long.toString(version)));
+ }
+
+ PublicKey key = signature.getPublicKey();
+ String fingerprint;
+ if (key instanceof OpenSshCertificate cert) {
+ fingerprint = KeyUtils.getFingerPrint(cert.getCertPubKey());
+ String message = SshCertificateUtils.verify(cert, signatureInstant);
+ if (message != null) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust, message);
+ }
+ } else {
+ fingerprint = KeyUtils.getFingerPrint(key);
+ }
+
+ String namespace = signature.getString();
+ if (!SshSignatureConstants.NAMESPACE.equals(namespace)) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust,
+ MessageFormat.format(SshdText.get().signInvalidNamespace,
+ namespace));
+ }
+
+ signature.getString(); // Skip the reserved field
+ String hashAlgorithm = signature.getString();
+ byte[] hash;
+ try {
+ hash = MessageDigest
+ .getInstance(hashAlgorithm.toUpperCase(Locale.ROOT))
+ .digest(data);
+ } catch (NoSuchAlgorithmException e) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust,
+ MessageFormat.format(
+ SshdText.get().signUnknownHashAlgorithm,
+ hashAlgorithm));
+ }
+ ByteArrayBuffer rawSignature = new ByteArrayBuffer(
+ signature.getBytes());
+ if (signature.available() > 0) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust,
+ SshdText.get().signGarbageAtEnd);
+ }
+
+ String signatureAlgorithm = rawSignature.getString();
+ switch (signatureAlgorithm) {
+ case KeyPairProvider.SSH_DSS:
+ case KeyPairProvider.SSH_DSS_CERT:
+ case KeyPairProvider.SSH_RSA:
+ case KeyPairProvider.SSH_RSA_CERT:
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust,
+ MessageFormat.format(SshdText.get().signInvalidAlgorithm,
+ signatureAlgorithm));
+ }
+
+ String keyType = KeyUtils
+ .getSignatureAlgorithm(KeyUtils.getKeyType(key), key);
+ if (!KeyUtils.getCanonicalKeyType(keyType)
+ .equals(KeyUtils.getCanonicalKeyType(signatureAlgorithm))) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust,
+ MessageFormat.format(
+ SshdText.get().signMismatchedSignatureAlgorithm,
+ keyType, signatureAlgorithm));
+ }
+
+ BuiltinSignatures factory = BuiltinSignatures
+ .fromFactoryName(signatureAlgorithm);
+ if (factory == null || !factory.isSupported()) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust,
+ MessageFormat.format(
+ SshdText.get().signUnknownSignatureAlgorithm,
+ signatureAlgorithm));
+ }
+
+ boolean valid;
+ String message = null;
+ try {
+ Signature verifier = factory.create();
+ verifier.initVerifier(null,
+ key instanceof OpenSshCertificate cert
+ ? cert.getCertPubKey()
+ : key);
+ // Feed it the data
+ Buffer toSign = new ByteArrayBuffer();
+ toSign.putRawBytes(SshSignatureConstants.MAGIC);
+ toSign.putString(SshSignatureConstants.NAMESPACE);
+ toSign.putUInt(0); // reserved: zero-length string
+ toSign.putString(hashAlgorithm);
+ toSign.putBytes(hash);
+ verifier.update(null, toSign.getCompactData());
+ valid = verifier.verify(null, rawSignature.getBytes());
+ } catch (Exception e) {
+ LOG.warn("{}", SshdText.get().signLogFailure, e); //$NON-NLS-1$
+ valid = false;
+ message = SshdText.get().signSeeLog;
+ }
+ boolean expired = false;
+ String principal = null;
+ if (valid) {
+ if (rawSignature.available() > 0) {
+ valid = false;
+ message = SshdText.get().signGarbageAtEnd;
+ } else {
+ SigningKeyDatabase database = SigningKeyDatabase.getInstance();
+ if (database.isRevoked(repository, config, key)) {
+ valid = false;
+ if (key instanceof OpenSshCertificate certificate) {
+ message = MessageFormat.format(
+ SshdText.get().signCertificateRevoked,
+ KeyUtils.getFingerPrint(
+ certificate.getCaPubKey()));
+ } else {
+ message = SshdText.get().signKeyRevoked;
+ }
+ } else {
+ // This may turn a positive verification into a failed one.
+ try {
+ principal = database.isAllowed(repository, config, key,
+ SshSignatureConstants.NAMESPACE, gitIdentity);
+ if (!StringUtils.isEmptyOrNull(principal)) {
+ trust = TrustLevel.FULL;
+ } else {
+ valid = false;
+ message = SshdText.get().signNoPrincipalMatched;
+ trust = TrustLevel.UNKNOWN;
+ }
+ } catch (VerificationException e) {
+ valid = false;
+ message = e.getMessage();
+ expired = e.isExpired();
+ } catch (IOException e) {
+ LOG.warn("{}", SshdText.get().signLogFailure, e); //$NON-NLS-1$
+ valid = false;
+ message = SshdText.get().signSeeLog;
+ }
+ }
+ }
+ }
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, principal, valid, expired, trust, message);
+ }
+
+ private static PersonIdent getGitIdentity(byte[] rawObject) {
+ // Data from a commit will start with "tree ID\n".
+ int i = RawParseUtils.match(rawObject, 0, TREE);
+ if (i > 0) {
+ i = RawParseUtils.committer(rawObject, 0);
+ if (i < 0) {
+ return null;
+ }
+ return RawParseUtils.parsePersonIdent(rawObject, i);
+ }
+ // Data from a tag will start with "object ID\ntype ".
+ i = RawParseUtils.match(rawObject, 0, OBJECT);
+ if (i > 0) {
+ i = RawParseUtils.nextLF(rawObject, i);
+ i = RawParseUtils.match(rawObject, i, TYPE);
+ if (i > 0) {
+ i = RawParseUtils.tagger(rawObject, 0);
+ if (i < 0) {
+ return null;
+ }
+ return RawParseUtils.parsePersonIdent(rawObject, i);
+ }
+ }
+ return null;
+ }
+
+ private static byte[] dearmor(byte[] data) {
+ int start = RawParseUtils.match(data, 0,
+ SshSignatureConstants.ARMOR_HEAD);
+ if (start > 0) {
+ if (data[start] == '\r') {
+ start++;
+ }
+ if (data[start] == '\n') {
+ start++;
+ }
+ }
+ int end = data.length;
+ if (end > start + 1 && data[end - 1] == '\n') {
+ end--;
+ if (end > start + 1 && data[end - 1] == '\r') {
+ end--;
+ }
+ }
+ end = end - SshSignatureConstants.ARMOR_END.length;
+ if (end >= 0 && end >= start
+ && RawParseUtils.match(data, end,
+ SshSignatureConstants.ARMOR_END) >= 0) {
+ // end is fine: on the first the character of the end marker
+ } else {
+ // No end marker.
+ end = data.length;
+ }
+ if (start < 0) {
+ start = 0;
+ }
+ return Base64.decode(data, start, end - start);
+ }
+
+ @Override
+ public void clear() {
+ SigningKeyDatabase database = SigningKeyDatabase.getInstance();
+ if (database instanceof CachingSigningKeyDatabase caching) {
+ caching.clearCache();
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSigner.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSigner.java
new file mode 100644
index 0000000000..8cfe5f4766
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSigner.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StreamCorruptedException;
+import java.io.StringReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity;
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.session.SessionContext;
+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.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.errors.CanceledException;
+import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
+import org.eclipse.jgit.internal.transport.sshd.AuthenticationCanceledException;
+import org.eclipse.jgit.internal.transport.sshd.PasswordProviderWrapper;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.internal.transport.sshd.agent.SshAgentClient;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgSignature;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Signer;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.sshd.KeyPasswordProviderFactory;
+import org.eclipse.jgit.transport.sshd.agent.Connector;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
+import org.eclipse.jgit.util.Base64;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.ExecutionResult;
+import org.eclipse.jgit.util.StringUtils;
+import org.eclipse.jgit.util.SystemReader;
+import org.eclipse.jgit.util.TemporaryBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link Signer} to create SSH signatures.
+ *
+ * @see <a href=
+ * "https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig">PROTOCOL.sshsig</a>
+ */
+public class SshSigner implements Signer {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SshSigner.class);
+
+ private static final String GIT_KEY_PREFIX = "key::"; //$NON-NLS-1$
+
+ // Base64 encoded lines should not be longer than 75 characters, plus the
+ // newline.
+ private static final int LINE_LENGTH = 75;
+
+ @Override
+ public GpgSignature sign(Repository repository, GpgConfig config,
+ byte[] data, PersonIdent committer, String signingKey,
+ CredentialsProvider credentialsProvider) throws CanceledException,
+ IOException, UnsupportedSigningFormatException {
+ byte[] hash;
+ try {
+ hash = MessageDigest.getInstance("SHA512").digest(data); //$NON-NLS-1$
+ } catch (NoSuchAlgorithmException e) {
+ throw new UnsupportedSigningFormatException(
+ MessageFormat.format(
+ SshdText.get().signUnknownHashAlgorithm, "SHA512"), //$NON-NLS-1$
+ e);
+ }
+ Buffer toSign = new ByteArrayBuffer();
+ toSign.putRawBytes(SshSignatureConstants.MAGIC);
+ toSign.putString(SshSignatureConstants.NAMESPACE);
+ toSign.putUInt(0); // reserved: zero-length string
+ toSign.putString("sha512"); //$NON-NLS-1$
+ toSign.putBytes(hash);
+ String key = signingKey;
+ if (StringUtils.isEmptyOrNull(key)) {
+ key = config.getSigningKey();
+ }
+ if (StringUtils.isEmptyOrNull(key)) {
+ key = defaultKeyCommand(repository, config);
+ // According to documentation, this is supposed to return a
+ // valid SSH public key prefixed with "key::". We don't enforce
+ // this: there might be older command implementations (like just
+ // calling "ssh-add -L") that return keys without prefix.
+ }
+ PublicKeyIdentity identity;
+ try {
+ identity = getIdentity(key, committer, credentialsProvider);
+ } catch (GeneralSecurityException e) {
+ throw new UnsupportedSigningFormatException(MessageFormat
+ .format(SshdText.get().signPublicKeyError, key), e);
+ }
+ String algorithm = KeyUtils
+ .getKeyType(identity.getKeyIdentity().getPublic());
+ switch (algorithm) {
+ case KeyPairProvider.SSH_DSS:
+ case KeyPairProvider.SSH_DSS_CERT:
+ throw new UnsupportedSigningFormatException(
+ SshdText.get().signInvalidKeyDSA);
+ case KeyPairProvider.SSH_RSA:
+ algorithm = KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS;
+ break;
+ case KeyPairProvider.SSH_RSA_CERT:
+ algorithm = KeyUtils.RSA_SHA512_CERT_TYPE_ALIAS;
+ break;
+ default:
+ break;
+ }
+
+ Map.Entry<String, byte[]> rawSignature;
+ try {
+ rawSignature = identity.sign(null, algorithm,
+ toSign.getCompactData());
+ } catch (Exception e) {
+ throw new UnsupportedSigningFormatException(
+ SshdText.get().signSignatureError, e);
+ }
+ algorithm = rawSignature.getKey();
+ Buffer signature = new ByteArrayBuffer();
+ signature.putRawBytes(SshSignatureConstants.MAGIC);
+ signature.putUInt(SshSignatureConstants.VERSION);
+ signature.putPublicKey(identity.getKeyIdentity().getPublic());
+ signature.putString(SshSignatureConstants.NAMESPACE);
+ signature.putUInt(0); // reserved: zero-length string
+ signature.putString("sha512"); //$NON-NLS-1$
+ Buffer sig = new ByteArrayBuffer();
+ sig.putString(KeyUtils.getSignatureAlgorithm(algorithm,
+ identity.getKeyIdentity().getPublic()));
+ sig.putBytes(rawSignature.getValue());
+ signature.putBytes(sig.getCompactData());
+ return armor(signature.getCompactData());
+ }
+
+ private static String defaultKeyCommand(@NonNull Repository repository,
+ @NonNull GpgConfig config) throws IOException {
+ String command = config.getSshDefaultKeyCommand();
+ if (StringUtils.isEmptyOrNull(command)) {
+ return null;
+ }
+ FS fileSystem = repository.getFS();
+ if (fileSystem == null) {
+ fileSystem = FS.DETECTED;
+ }
+ ProcessBuilder builder = fileSystem.runInShell(command,
+ new String[] {});
+ ExecutionResult result = null;
+ try {
+ result = fileSystem.execute(builder, null);
+ int exitCode = result.getRc();
+ if (exitCode == 0) {
+ // The command is supposed to return a public key in its first
+ // line on stdout.
+ try (BufferedReader r = new BufferedReader(
+ new InputStreamReader(
+ result.getStdout().openInputStream(),
+ SystemReader.getInstance()
+ .getDefaultCharset()))) {
+ String line = r.readLine();
+ if (line != null) {
+ line = line.strip();
+ }
+ if (StringUtils.isEmptyOrNull(line)) {
+ throw new IOException(MessageFormat.format(
+ SshdText.get().signDefaultKeyEmpty, command));
+ }
+ return line;
+ }
+ }
+ TemporaryBuffer stderr = result.getStderr();
+ throw new IOException(MessageFormat.format(
+ SshdText.get().signDefaultKeyFailed, command,
+ Integer.toString(exitCode), toString(stderr)));
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IOException(
+ MessageFormat.format(
+ SshdText.get().signDefaultKeyInterrupted, command),
+ e);
+ } finally {
+ if (result != null) {
+ if (result.getStderr() != null) {
+ result.getStderr().destroy();
+ }
+ if (result.getStdout() != null) {
+ result.getStdout().destroy();
+ }
+ }
+ }
+ }
+
+ private static String toString(TemporaryBuffer b) {
+ if (b != null) {
+ try {
+ return new String(b.toByteArray(4000),
+ SystemReader.getInstance().getDefaultCharset());
+ } catch (IOException e) {
+ LOG.warn("{}", SshdText.get().signStderr, e); //$NON-NLS-1$
+ }
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ private static PublicKeyIdentity getIdentity(String signingKey,
+ PersonIdent committer, CredentialsProvider credentials)
+ throws CanceledException, GeneralSecurityException, IOException {
+ if (StringUtils.isEmptyOrNull(signingKey)) {
+ throw new IllegalArgumentException(SshdText.get().signNoSigningKey);
+ }
+ PublicKey publicKey = null;
+ PrivateKey privateKey = null;
+ File keyFile = null;
+ if (signingKey.startsWith(GIT_KEY_PREFIX)) {
+ try (StringReader r = new StringReader(
+ signingKey.substring(GIT_KEY_PREFIX.length()))) {
+ publicKey = fromEntry(
+ AuthorizedKeyEntry.readAuthorizedKeys(r, true));
+ }
+ } else if (signingKey.startsWith("~/") //$NON-NLS-1$
+ || signingKey.startsWith('~' + File.separator)) {
+ keyFile = new File(FS.DETECTED.userHome(), signingKey.substring(2));
+ } else {
+ try (StringReader r = new StringReader(signingKey)) {
+ publicKey = fromEntry(
+ AuthorizedKeyEntry.readAuthorizedKeys(r, true));
+ } catch (IOException e) {
+ // Ignore and try to read as a file
+ keyFile = new File(signingKey);
+ }
+ }
+ if (keyFile != null && keyFile.isFile()) {
+ try {
+ publicKey = fromEntry(AuthorizedKeyEntry
+ .readAuthorizedKeys(keyFile.toPath()));
+ if (publicKey == null) {
+ throw new IOException(MessageFormat.format(
+ SshdText.get().signTooManyPublicKeys, keyFile));
+ }
+ // Try to find the private key so we don't go looking for
+ // the agent (or PKCS#11) in vain.
+ keyFile = getPrivateKeyFile(keyFile.getParentFile(),
+ keyFile.getName());
+ if (keyFile != null) {
+ try {
+ KeyPair pair = loadPrivateKey(keyFile.toPath(),
+ credentials);
+ if (pair != null) {
+ PublicKey pk = pair.getPublic();
+ if (pk == null) {
+ privateKey = pair.getPrivate();
+ } else {
+ PublicKey original = publicKey;
+ if (publicKey instanceof OpenSshCertificate cert) {
+ original = cert.getCertPubKey();
+ }
+ if (KeyUtils.compareKeys(original, pk)) {
+ privateKey = pair.getPrivate();
+ }
+ }
+ }
+ } catch (IOException e) {
+ // Apparently it wasn't a private key file. Ignore.
+ }
+ }
+ } catch (StreamCorruptedException e) {
+ // File is readable, but apparently not a public key. Try to
+ // load it as a private key.
+ KeyPair pair = loadPrivateKey(keyFile.toPath(), credentials);
+ if (pair != null) {
+ publicKey = pair.getPublic();
+ privateKey = pair.getPrivate();
+ }
+ }
+ }
+ if (publicKey == null) {
+ throw new IOException(MessageFormat
+ .format(SshdText.get().signNoPublicKey, signingKey));
+ }
+ if (publicKey instanceof OpenSshCertificate cert) {
+ String message = SshCertificateUtils.verify(cert,
+ committer.getWhenAsInstant());
+ if (message != null) {
+ throw new IOException(message);
+ }
+ }
+ if (privateKey == null) {
+ // Could be in the agent, or a PKCS#11 key. The normal procedure
+ // with PKCS#11 keys is to put them in the agent and let the agent
+ // deal with it.
+ //
+ // This may or may not work well. For instance, the agent might ask
+ // for a passphrase for PKCS#11 keys... also, the OpenSSH ssh-agent
+ // had a bug with signing using PKCS#11 certificates in the agent;
+ // see https://bugzilla.mindrot.org/show_bug.cgi?id=3613 . If there
+ // are troubles, we might do the PKCS#11 dance ourselves, but we'd
+ // need additional configuration for the PKCS#11 library. (Plus
+ // some refactoring in the Pkcs11Provider.)
+ return new AgentIdentity(publicKey);
+
+ }
+ return new KeyPairIdentity(new KeyPair(publicKey, privateKey));
+ }
+
+ private static File getPrivateKeyFile(File directory,
+ String publicKeyName) {
+ if (publicKeyName.endsWith(".pub")) { //$NON-NLS-1$
+ String privateKeyName = publicKeyName.substring(0,
+ publicKeyName.length() - 4);
+ if (!privateKeyName.isEmpty()) {
+ File keyFile = new File(directory, privateKeyName);
+ if (keyFile.isFile()) {
+ return keyFile;
+ }
+ if (privateKeyName.endsWith("-cert")) { //$NON-NLS-1$
+ privateKeyName = privateKeyName.substring(0,
+ privateKeyName.length() - 5);
+ if (!privateKeyName.isEmpty()) {
+ keyFile = new File(directory, privateKeyName);
+ if (keyFile.isFile()) {
+ return keyFile;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static KeyPair loadPrivateKey(Path path,
+ CredentialsProvider credentials)
+ throws CanceledException, GeneralSecurityException, IOException {
+ if (!Files.isRegularFile(path)) {
+ return null;
+ }
+ KeyPairResourceParser parser = SecurityUtils.getKeyPairResourceParser();
+ if (parser != null) {
+ PasswordProviderWrapper provider = null;
+ if (credentials != null) {
+ provider = new PasswordProviderWrapper(
+ () -> KeyPasswordProviderFactory.getInstance()
+ .apply(credentials));
+ }
+ try {
+ Collection<KeyPair> keyPairs = parser.loadKeyPairs(null, path,
+ provider);
+ if (keyPairs.size() != 1) {
+ throw new GeneralSecurityException(MessageFormat.format(
+ SshdText.get().signTooManyPrivateKeys, path));
+ }
+ return keyPairs.iterator().next();
+ } catch (AuthenticationCanceledException e) {
+ throw new CanceledException(e.getMessage());
+ }
+ }
+ return null;
+ }
+
+ private static GpgSignature armor(byte[] data) throws IOException {
+ try (ByteArrayOutputStream b = new ByteArrayOutputStream()) {
+ b.write(SshSignatureConstants.ARMOR_HEAD);
+ b.write('\n');
+ String encoded = Base64.encodeBytes(data);
+ int length = encoded.length();
+ int column = 0;
+ for (int i = 0; i < length; i++) {
+ b.write(encoded.charAt(i));
+ column++;
+ if (column == LINE_LENGTH) {
+ b.write('\n');
+ column = 0;
+ }
+ }
+ if (column > 0) {
+ b.write('\n');
+ }
+ b.write(SshSignatureConstants.ARMOR_END);
+ b.write('\n');
+ return new GpgSignature(b.toByteArray());
+ }
+ }
+
+ private static PublicKey fromEntry(List<AuthorizedKeyEntry> entries)
+ throws GeneralSecurityException, IOException {
+ if (entries == null || entries.size() != 1) {
+ return null;
+ }
+ return entries.get(0).resolvePublicKey(null,
+ PublicKeyEntryResolver.FAILING);
+ }
+
+ @Override
+ public boolean canLocateSigningKey(Repository repository, GpgConfig config,
+ PersonIdent committer, String signingKey,
+ CredentialsProvider credentialsProvider) throws CanceledException {
+ String key = signingKey;
+ if (key == null) {
+ key = config.getSigningKey();
+ }
+ return !(StringUtils.isEmptyOrNull(key)
+ && StringUtils.isEmptyOrNull(config.getSshDefaultKeyCommand()));
+ }
+
+ private static class KeyPairIdentity implements PublicKeyIdentity {
+
+ private final @NonNull KeyPair pair;
+
+ KeyPairIdentity(@NonNull KeyPair pair) {
+ this.pair = pair;
+ }
+
+ @Override
+ public KeyPair getKeyIdentity() {
+ return pair;
+ }
+
+ @Override
+ public Entry<String, byte[]> sign(SessionContext session, String algo,
+ byte[] data) throws Exception {
+ BuiltinSignatures factory = BuiltinSignatures.fromFactoryName(algo);
+ if (factory == null || !factory.isSupported()) {
+ throw new GeneralSecurityException(MessageFormat.format(
+ SshdText.get().signUnknownSignatureAlgorithm, algo));
+ }
+ Signature signer = factory.create();
+ signer.initSigner(null, pair.getPrivate());
+ signer.update(null, data);
+ return new SimpleImmutableEntry<>(factory.getName(),
+ signer.sign(null));
+ }
+ }
+
+ private static class AgentIdentity extends KeyPairIdentity {
+
+ AgentIdentity(PublicKey publicKey) {
+ super(new KeyPair(publicKey, null));
+ }
+
+ @Override
+ public Entry<String, byte[]> sign(SessionContext session, String algo,
+ byte[] data) throws Exception {
+ ConnectorFactory factory = ConnectorFactory.getDefault();
+ Connector connector = factory == null ? null
+ : factory.create("", null); //$NON-NLS-1$
+ if (connector == null) {
+ throw new IOException(SshdText.get().signNoAgent);
+ }
+ try (SshAgentClient agent = new SshAgentClient(connector)) {
+ return agent.sign(null, getKeyIdentity().getPublic(), algo,
+ data);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
index b0b1028daa..6aace4753a 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
@@ -17,6 +17,7 @@ import static org.eclipse.jgit.transport.SshConstants.PUBKEY_ACCEPTED_ALGORITHMS
import java.io.File;
import java.io.IOException;
+import java.io.StreamCorruptedException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
@@ -355,20 +356,20 @@ public class JGitPublicKeyAuthentication extends UserAuthPublicKey {
// only warn about non-existing files in case the key file is
// not derived
if (!isDerived) {
- log.warn("{}", //$NON-NLS-1$
+ log.warn(LOG_FORMAT,
format(SshdText.get().cannotReadPublicKey, keyFile));
}
- } catch (InvalidPathException | IOException e) {
- log.warn("{}", //$NON-NLS-1$
- format(SshdText.get().cannotReadPublicKey, keyFile), e);
- } catch (GeneralSecurityException e) {
+ } catch (GeneralSecurityException | StreamCorruptedException e) {
// ignore in case this is not a derived key path, as in most
// cases this specifies a private key
if (isDerived) {
- log.warn("{}", //$NON-NLS-1$
+ log.warn(LOG_FORMAT,
format(SshdText.get().cannotReadPublicKey, keyFile),
e);
}
+ } catch (InvalidPathException | IOException e) {
+ log.warn(LOG_FORMAT,
+ format(SshdText.get().cannotReadPublicKey, keyFile), e);
}
return null;
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java
index 96829b7365..6b2345df1b 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java
@@ -29,6 +29,7 @@ import org.apache.sshd.client.config.hosts.HostPatternsHolder;
import org.apache.sshd.client.config.hosts.KnownHostEntry;
import org.apache.sshd.client.config.hosts.KnownHostHashValue;
import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -97,7 +98,7 @@ public class KnownHostEntryReader {
return i < 0 ? line.trim() : line.substring(0, i).trim();
}
- private static KnownHostEntry parseHostEntry(String line) {
+ static KnownHostEntry parseHostEntry(String line) {
KnownHostEntry entry = new KnownHostEntry();
entry.setConfigLine(line);
String tmp = line;
@@ -135,8 +136,8 @@ public class KnownHostEntryReader {
entry.setPatterns(patterns);
}
tmp = tmp.substring(i + 1).trim();
- AuthorizedKeyEntry key = AuthorizedKeyEntry
- .parseAuthorizedKeyEntry(tmp);
+ AuthorizedKeyEntry key = PublicKeyEntry
+ .parsePublicKeyEntry(new AuthorizedKeyEntry(), tmp);
if (key == null) {
return null;
}
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 2b4f7e50ff..acb77c5bb7 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2025 Thomas Wolf <twolf@apache.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,9 +31,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
+import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
@@ -45,10 +47,13 @@ import org.apache.sshd.client.config.hosts.KnownHostHashValue;
import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier.HostEntryPair;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.NamedFactory;
+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.OpenSshCertificate;
import org.apache.sshd.common.config.keys.PublicKeyEntry;
import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.config.keys.UnsupportedSshPublicKey;
import org.apache.sshd.common.digest.BuiltinDigests;
import org.apache.sshd.common.mac.Mac;
import org.apache.sshd.common.util.io.ModifiableFileWatcher;
@@ -126,6 +131,9 @@ public class OpenSshServerKeyDatabase
/** Can be used to mark revoked known host lines. */
private static final String MARKER_REVOKED = "revoked"; //$NON-NLS-1$
+ /** Marks CA keys used for SSH certificates. */
+ private static final String MARKER_CA = "cert-authority"; //$NON-NLS-1$
+
private final boolean askAboutNewFile;
private final Map<Path, HostKeyFile> knownHostsFiles = new ConcurrentHashMap<>();
@@ -178,7 +186,10 @@ public class OpenSshServerKeyDatabase
for (HostKeyFile file : filesToUse) {
for (HostEntryPair current : file.get()) {
KnownHostEntry entry = current.getHostEntry();
- if (!isRevoked(entry)) {
+ if (current.getServerKey() instanceof UnsupportedSshPublicKey) {
+ continue;
+ }
+ if (!isRevoked(entry) && !isCertificateAuthority(entry)) {
for (SshdSocketAddress host : candidates) {
if (entry.isHostMatch(host.getHostName(),
host.getPort())) {
@@ -204,6 +215,7 @@ public class OpenSshServerKeyDatabase
Collection<SshdSocketAddress> candidates = getCandidates(connectAddress,
remoteAddress);
for (HostKeyFile file : filesToUse) {
+ HostEntryPair lastModified = modified[0];
try {
if (find(candidates, serverKey, file.get(), modified)) {
return true;
@@ -212,24 +224,35 @@ public class OpenSshServerKeyDatabase
ask.revokedKey(remoteAddress, serverKey, file.getPath());
return false;
}
- if (path == null && modified[0] != null) {
+ if (modified[0] != lastModified) {
// Remember the file in which we might need to update the
// entry
path = file.getPath();
}
}
+ if (serverKey instanceof OpenSshCertificate) {
+ return false;
+ }
if (modified[0] != null) {
- // We found an entry, but with a different key
+ // We found an entry, but with a different key.
AskUser.ModifiedKeyHandling toDo = ask.acceptModifiedServerKey(
remoteAddress, modified[0].getServerKey(),
serverKey, path);
if (toDo == AskUser.ModifiedKeyHandling.ALLOW_AND_STORE) {
- try {
- updateModifiedServerKey(serverKey, modified[0], path);
- knownHostsFiles.get(path).resetReloadAttributes();
- } catch (IOException e) {
- LOG.warn(format(SshdText.get().knownHostsCouldNotUpdate,
- path));
+ if (modified[0]
+ .getServerKey() instanceof UnsupportedSshPublicKey) {
+ // Never update a line containing an unknown key type,
+ // always add.
+ addKeyToFile(filesToUse.get(0), candidates, serverKey, ask,
+ config);
+ } else {
+ try {
+ updateModifiedServerKey(serverKey, modified[0], path);
+ knownHostsFiles.get(path).resetReloadAttributes();
+ } catch (IOException e) {
+ LOG.warn(format(SshdText.get().knownHostsCouldNotUpdate,
+ path));
+ }
}
}
if (toDo == AskUser.ModifiedKeyHandling.DENY) {
@@ -242,19 +265,8 @@ public class OpenSshServerKeyDatabase
return true;
} else if (ask.acceptUnknownKey(remoteAddress, serverKey)) {
if (!filesToUse.isEmpty()) {
- HostKeyFile toUpdate = filesToUse.get(0);
- path = toUpdate.getPath();
- try {
- if (Files.exists(path) || !askAboutNewFile
- || ask.createNewFile(path)) {
- updateKnownHostsFile(candidates, serverKey, path,
- config);
- toUpdate.resetReloadAttributes();
- }
- } catch (Exception e) {
- LOG.warn(format(SshdText.get().knownHostsCouldNotUpdate,
- path), e);
- }
+ addKeyToFile(filesToUse.get(0), candidates, serverKey, ask,
+ config);
}
return true;
}
@@ -265,39 +277,90 @@ public class OpenSshServerKeyDatabase
private static final long serialVersionUID = 1L;
}
- private boolean isRevoked(KnownHostEntry entry) {
+ private static boolean isRevoked(KnownHostEntry entry) {
return MARKER_REVOKED.equals(entry.getMarker());
}
+ private static boolean isCertificateAuthority(KnownHostEntry entry) {
+ return MARKER_CA.equals(entry.getMarker());
+ }
+
private boolean find(Collection<SshdSocketAddress> candidates,
PublicKey serverKey, List<HostEntryPair> entries,
HostEntryPair[] modified) throws RevokedKeyException {
+ PublicKey keyToCheck = serverKey;
+ boolean isCert = false;
+ String keyType = KeyUtils.getKeyType(keyToCheck);
+ String modifiedKeyType = null;
+ if (modified[0] != null) {
+ modifiedKeyType = modified[0].getHostEntry().getKeyEntry()
+ .getKeyType();
+ }
+ if (serverKey instanceof OpenSshCertificate) {
+ keyToCheck = ((OpenSshCertificate) serverKey).getCaPubKey();
+ isCert = true;
+ }
for (HostEntryPair current : entries) {
KnownHostEntry entry = current.getHostEntry();
- for (SshdSocketAddress host : candidates) {
- if (entry.isHostMatch(host.getHostName(), host.getPort())) {
- boolean revoked = isRevoked(entry);
- if (KeyUtils.compareKeys(serverKey,
- current.getServerKey())) {
- // Exact match
- if (revoked) {
- throw new RevokedKeyException();
- }
+ if (candidates.stream().anyMatch(host -> entry
+ .isHostMatch(host.getHostName(), host.getPort()))) {
+ boolean revoked = isRevoked(entry);
+ boolean haveCert = isCertificateAuthority(entry);
+ if (KeyUtils.compareKeys(keyToCheck, current.getServerKey())) {
+ // Exact match
+ if (revoked) {
+ throw new RevokedKeyException();
+ }
+ if (haveCert == isCert) {
modified[0] = null;
return true;
- } else if (!revoked) {
- // Server sent a different key
+ }
+ }
+ if (haveCert == isCert && !haveCert && !revoked) {
+ // Server sent a different key.
+ if (modifiedKeyType == null) {
modified[0] = current;
- // Keep going -- maybe there's another entry for this
- // host
+ modifiedKeyType = entry.getKeyEntry().getKeyType();
+ } else if (!keyType.equals(modifiedKeyType)) {
+ String thisKeyType = entry.getKeyEntry().getKeyType();
+ if (isBetterMatch(keyType, thisKeyType,
+ modifiedKeyType)) {
+ // Since we may replace the modified[0] key,
+ // prefer to report a key of the same key type
+ // as having been modified.
+ modified[0] = current;
+ modifiedKeyType = keyType;
+ }
}
- break;
+ // Keep going -- maybe there's another entry for this
+ // host
}
}
}
return false;
}
+ private static boolean isBetterMatch(String keyType, String thisType,
+ String modifiedType) {
+ if (keyType.equals(thisType)) {
+ return true;
+ }
+ // EC keys are a bit special because they encode the curve in the key
+ // type. If we have no exactly matching EC key type in known_hosts, we
+ // still prefer to update an existing EC key type over some other key
+ // type.
+ if (!keyType.startsWith("ecdsa") || !thisType.startsWith("ecdsa")) { //$NON-NLS-1$ //$NON-NLS-2$
+ return false;
+ }
+ if (!modifiedType.startsWith("ecdsa")) { //$NON-NLS-1$
+ return true;
+ }
+ // All three are EC keys. thisType doesn't match the size of keyType
+ // (otherwise the two would have compared equal above already), so it is
+ // not better than modifiedType.
+ return false;
+ }
+
private List<HostKeyFile> addUserHostKeyFiles(List<String> fileNames) {
if (fileNames == null || fileNames.isEmpty()) {
return Collections.emptyList();
@@ -317,6 +380,21 @@ public class OpenSshServerKeyDatabase
return userFiles;
}
+ private void addKeyToFile(HostKeyFile file,
+ Collection<SshdSocketAddress> candidates, PublicKey serverKey,
+ AskUser ask, Configuration config) {
+ Path path = file.getPath();
+ try {
+ if (Files.exists(path) || !askAboutNewFile
+ || ask.createNewFile(path)) {
+ updateKnownHostsFile(candidates, serverKey, path, config);
+ file.resetReloadAttributes();
+ }
+ } catch (Exception e) {
+ LOG.warn(format(SshdText.get().knownHostsCouldNotUpdate, path), e);
+ }
+ }
+
private void updateKnownHostsFile(Collection<SshdSocketAddress> candidates,
PublicKey serverKey, Path path, Configuration config)
throws Exception {
@@ -453,15 +531,22 @@ public class OpenSshServerKeyDatabase
return;
}
InetSocketAddress remote = (InetSocketAddress) remoteAddress;
+ boolean isCert = serverKey instanceof OpenSshCertificate;
+ PublicKey keyToReport = isCert
+ ? ((OpenSshCertificate) serverKey).getCaPubKey()
+ : serverKey;
URIish uri = JGitUserInteraction.toURI(config.getUsername(),
remote);
String sha256 = KeyUtils.getFingerPrint(BuiltinDigests.sha256,
- serverKey);
- String md5 = KeyUtils.getFingerPrint(BuiltinDigests.md5, serverKey);
- String keyAlgorithm = serverKey.getAlgorithm();
+ keyToReport);
+ String md5 = KeyUtils.getFingerPrint(BuiltinDigests.md5,
+ keyToReport);
+ String keyAlgorithm = keyToReport.getAlgorithm();
+ String msg = isCert
+ ? SshdText.get().knownHostsRevokedCertificateMsg
+ : SshdText.get().knownHostsRevokedKeyMsg;
askUser(provider, uri, null, //
- format(SshdText.get().knownHostsRevokedKeyMsg,
- remote.getHostString(), path),
+ format(msg, remote.getHostString(), path),
format(SshdText.get().knownHostsKeyFingerprints,
keyAlgorithm),
md5, sha256);
@@ -594,7 +679,7 @@ public class OpenSshServerKeyDatabase
}
try {
PublicKey serverKey = keyPart.resolvePublicKey(null,
- PublicKeyEntryResolver.IGNORING);
+ PublicKeyEntryResolver.UNSUPPORTED);
if (serverKey == null) {
LOG.warn(format(
SshdText.get().knownHostsUnknownKeyType,
@@ -625,7 +710,7 @@ public class OpenSshServerKeyDatabase
private SshdSocketAddress toSshdSocketAddress(@NonNull String address) {
String host = null;
- int port = 0;
+ int port = SshConstants.DEFAULT_PORT;
if (HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM == address
.charAt(0)) {
int end = address.indexOf(
@@ -665,12 +750,23 @@ public class OpenSshServerKeyDatabase
if (address != null) {
candidates.add(address);
}
- return candidates;
+ List<SshdSocketAddress> result = new ArrayList<>();
+ result.addAll(candidates);
+ if (!remoteAddress.isUnresolved()) {
+ SshdSocketAddress ip = new SshdSocketAddress(
+ remoteAddress.getAddress().getHostAddress(),
+ remoteAddress.getPort());
+ if (candidates.add(ip)) {
+ result.add(ip);
+ }
+ }
+ return result;
}
private String createHostKeyLine(Collection<SshdSocketAddress> patterns,
PublicKey key, Configuration config) throws Exception {
StringBuilder result = new StringBuilder();
+ Set<String> knownNames = new HashSet<>();
if (config.getHashKnownHosts()) {
// SHA1 is the only algorithm for host name hashing known to OpenSSH
// or to Apache MINA sshd.
@@ -680,10 +776,10 @@ public class OpenSshServerKeyDatabase
prng = new SecureRandom();
}
byte[] salt = new byte[mac.getDefaultBlockSize()];
- for (SshdSocketAddress address : patterns) {
- if (result.length() > 0) {
- result.append(',');
- }
+ // For hashed hostnames, only one hashed pattern is allowed per
+ // https://man.openbsd.org/sshd.8#SSH_KNOWN_HOSTS_FILE_FORMAT
+ if (!patterns.isEmpty()) {
+ SshdSocketAddress address = patterns.iterator().next();
prng.nextBytes(salt);
KnownHostHashValue.append(result, digester, salt,
KnownHostHashValue.calculateHashValue(
@@ -692,6 +788,10 @@ public class OpenSshServerKeyDatabase
}
} else {
for (SshdSocketAddress address : patterns) {
+ String tgt = address.getHostName() + ':' + address.getPort();
+ if (!knownNames.add(tgt)) {
+ continue;
+ }
if (result.length() > 0) {
result.append(',');
}
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 2cd0669842..900c9fba24 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2024 Thomas Wolf <twolf@apache.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
@@ -47,6 +47,8 @@ public class PasswordProviderWrapper implements FilePasswordProvider {
private final Supplier<KeyPasswordProvider> factory;
+ private PerSessionState noSessionState;
+
/**
* Creates a new {@link PasswordProviderWrapper}.
*
@@ -59,13 +61,18 @@ public class PasswordProviderWrapper implements FilePasswordProvider {
}
private PerSessionState getState(SessionContext context) {
- PerSessionState state = context.getAttribute(STATE);
+ PerSessionState state = context != null ? context.getAttribute(STATE)
+ : noSessionState;
if (state == null) {
state = new PerSessionState();
state.delegate = factory.get();
state.delegate.setAttempts(
PASSWORD_PROMPTS.getRequiredDefault().intValue());
- context.setAttribute(STATE, state);
+ if (context != null) {
+ context.setAttribute(STATE, state);
+ } else {
+ noSessionState = state;
+ }
}
return state;
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
index 05f04ac5b2..e40137870b 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2024 Thomas Wolf <twolf@apache.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
@@ -83,6 +83,7 @@ public final class SshdText extends TranslationBundle {
/***/ public String knownHostsModifiedKeyDenyMsg;
/***/ public String knownHostsModifiedKeyStorePrompt;
/***/ public String knownHostsModifiedKeyWarning;
+ /***/ public String knownHostsRevokedCertificateMsg;
/***/ public String knownHostsRevokedKeyMsg;
/***/ public String knownHostsUnknownKeyMsg;
/***/ public String knownHostsUnknownKeyPrompt;
@@ -147,6 +148,71 @@ public final class SshdText extends TranslationBundle {
/***/ public String sshCommandTimeout;
/***/ public String sshProcessStillRunning;
/***/ public String sshProxySessionCloseFailed;
+ /***/ public String signAllowedSignersCertAuthorityError;
+ /***/ public String signAllowedSignersEmptyIdentity;
+ /***/ public String signAllowedSignersEmptyNamespaces;
+ /***/ public String signAllowedSignersFormatError;
+ /***/ public String signAllowedSignersInvalidDate;
+ /***/ public String signAllowedSignersLineFormat;
+ /***/ public String signAllowedSignersMultiple;
+ /***/ public String signAllowedSignersNoIdentities;
+ /***/ public String signAllowedSignersPublicKeyParsing;
+ /***/ public String signAllowedSignersUnterminatedQuote;
+ /***/ public String signCertAlgorithmMismatch;
+ /***/ public String signCertAlgorithmUnknown;
+ /***/ public String signCertificateExpired;
+ /***/ public String signCertificateInvalid;
+ /***/ public String signCertificateNotForName;
+ /***/ public String signCertificateRevoked;
+ /***/ public String signCertificateTooEarly;
+ /***/ public String signCertificateWithoutPrincipals;
+ /***/ public String signDefaultKeyEmpty;
+ /***/ public String signDefaultKeyFailed;
+ /***/ public String signDefaultKeyInterrupted;
+ /***/ public String signGarbageAtEnd;
+ /***/ public String signInvalidAlgorithm;
+ /***/ public String signInvalidKeyDSA;
+ /***/ public String signInvalidMagic;
+ /***/ public String signInvalidNamespace;
+ /***/ public String signInvalidSignature;
+ /***/ public String signInvalidVersion;
+ /***/ public String signKeyExpired;
+ /***/ public String signKeyRevoked;
+ /***/ public String signKeyTooEarly;
+ /***/ public String signKrlBlobLeftover;
+ /***/ public String signKrlBlobLengthInvalid;
+ /***/ public String signKrlBlobLengthInvalidExpected;
+ /***/ public String signKrlCaKeyLengthInvalid;
+ /***/ public String signKrlCertificateLeftover;
+ /***/ public String signKrlCertificateSubsectionLeftover;
+ /***/ public String signKrlCertificateSubsectionLength;
+ /***/ public String signKrlEmptyRange;
+ /***/ public String signKrlInvalidBitSetLength;
+ /***/ public String signKrlInvalidKeyIdLength;
+ /***/ public String signKrlInvalidMagic;
+ /***/ public String signKrlInvalidReservedLength;
+ /***/ public String signKrlInvalidVersion;
+ /***/ public String signKrlNoCertificateSubsection;
+ /***/ public String signKrlSerialZero;
+ /***/ public String signKrlShortRange;
+ /***/ public String signKrlUnknownSection;
+ /***/ public String signKrlUnknownSubsection;
+ /***/ public String signLogFailure;
+ /***/ public String signMismatchedSignatureAlgorithm;
+ /***/ public String signNoAgent;
+ /***/ public String signNoPrincipalMatched;
+ /***/ public String signNoPublicKey;
+ /***/ public String signNoSigningKey;
+ /***/ public String signNotUserCertificate;
+ /***/ public String signPublicKeyError;
+ /***/ public String signSeeLog;
+ /***/ public String signSignatureError;
+ /***/ public String signStderr;
+ /***/ public String signTooManyPrivateKeys;
+ /***/ public String signTooManyPublicKeys;
+ /***/ public String signUnknownHashAlgorithm;
+ /***/ public String signUnknownSignatureAlgorithm;
+ /***/ public String signWrongNamespace;
/***/ public String unknownProxyProtocol;
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
index 8866976c89..3e1fab34d9 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
@@ -17,8 +17,6 @@ import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.concurrent.CancellationException;
@@ -113,13 +111,12 @@ public abstract class BasicAuthentication<ParameterType, TokenType>
*/
protected void askCredentials() {
clearPassword();
- PasswordAuthentication auth = AccessController.doPrivileged(
- (PrivilegedAction<PasswordAuthentication>) () -> Authenticator
- .requestPasswordAuthentication(proxy.getHostString(),
- proxy.getAddress(), proxy.getPort(),
- SshConstants.SSH_SCHEME,
- SshdText.get().proxyPasswordPrompt, "Basic", //$NON-NLS-1$
- null, RequestorType.PROXY));
+ PasswordAuthentication auth = Authenticator
+ .requestPasswordAuthentication(proxy.getHostString(),
+ proxy.getAddress(), proxy.getPort(),
+ SshConstants.SSH_SCHEME,
+ SshdText.get().proxyPasswordPrompt, "Basic", //$NON-NLS-1$
+ null, RequestorType.PROXY);
if (auth == null) {
user = ""; //$NON-NLS-1$
throw new CancellationException(
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/CachingSigningKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/CachingSigningKeyDatabase.java
new file mode 100644
index 0000000000..4d2d8b6797
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/CachingSigningKeyDatabase.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.signing.ssh;
+
+/**
+ * A {@link SigningKeyDatabase} that caches data.
+ * <p>
+ * A signing key database may be used to check keys frequently; it may thus need
+ * to cache some data and it may need to cache data per repository. If an
+ * implementation does cache data, it is responsible itself for refreshing that
+ * cache at appropriate times. Clients can control the cache size somewhat via
+ * {@link #setCacheSize(int)}, although the meaning of the cache size (i.e., its
+ * unit) is left undefined here.
+ * </p>
+ *
+ * @since 7.1
+ */
+public interface CachingSigningKeyDatabase extends SigningKeyDatabase {
+
+ /**
+ * Retrieves the current cache size.
+ *
+ * @return the cache size, or -1 if this database has no cache.
+ */
+ int getCacheSize();
+
+ /**
+ * Sets the cache size to use.
+ *
+ * @param size
+ * the cache size, ignored if this database does not have a
+ * cache.
+ * @throws IllegalArgumentException
+ * if {@code size < 0}
+ */
+ void setCacheSize(int size);
+
+ /**
+ * Discards any cached data. A no-op if the database has no cache.
+ */
+ void clearCache();
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SigningKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SigningKeyDatabase.java
new file mode 100644
index 0000000000..eec64c3abd
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SigningKeyDatabase.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.signing.ssh;
+
+import java.io.IOException;
+import java.security.PublicKey;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.signing.ssh.SigningDatabase;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * A database storing meta-information about signing keys and certificates.
+ *
+ * @since 7.1
+ */
+public interface SigningKeyDatabase {
+
+ /**
+ * Obtains the current global instance.
+ *
+ * @return the global {@link SigningKeyDatabase}
+ */
+ static SigningKeyDatabase getInstance() {
+ return SigningDatabase.getInstance();
+ }
+
+ /**
+ * Sets the global {@link SigningKeyDatabase}.
+ *
+ * @param database
+ * to set; if {@code null} a default database using the OpenSSH
+ * allowed signers file and the OpenSSH revocation list mechanism
+ * is used.
+ * @return the previously set {@link SigningKeyDatabase}
+ */
+ static SigningKeyDatabase setInstance(SigningKeyDatabase database) {
+ return SigningDatabase.setInstance(database);
+ }
+
+ /**
+ * Determines whether the gives key has been revoked.
+ *
+ * @param repository
+ * {@link Repository} the key is being used in
+ * @param config
+ * {@link GpgConfig} to use
+ * @param key
+ * {@link PublicKey} to check
+ * @return {@code true} if the key has been revoked, {@code false} otherwise
+ * @throws IOException
+ * if an I/O problem occurred
+ */
+ boolean isRevoked(@NonNull Repository repository, @NonNull GpgConfig config,
+ @NonNull PublicKey key) throws IOException;
+
+ /**
+ * Checks whether the given key is allowed to be used for signing, and if
+ * allowed returns the principal.
+ *
+ * @param repository
+ * {@link Repository} the key is being used in
+ * @param config
+ * {@link GpgConfig} to use
+ * @param key
+ * {@link PublicKey} to check
+ * @param namespace
+ * of the signature
+ * @param ident
+ * optional {@link PersonIdent} giving a signer's e-mail address
+ * and a signature time
+ * @return {@code null} if the database does not contain any information
+ * about the given key; the principal if it does and all checks
+ * passed
+ * @throws IOException
+ * if an I/O problem occurred
+ * @throws VerificationException
+ * if the database contains information about the key and the
+ * checks determined that the key is not allowed to be used for
+ * signing
+ */
+ String isAllowed(@NonNull Repository repository, @NonNull GpgConfig config,
+ @NonNull PublicKey key, @NonNull String namespace,
+ PersonIdent ident) throws IOException, VerificationException;
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignatureVerifierFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignatureVerifierFactory.java
new file mode 100644
index 0000000000..c315428c33
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignatureVerifierFactory.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.signing.ssh;
+
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.SignatureVerifier;
+import org.eclipse.jgit.internal.signing.ssh.SshSignatureVerifier;
+import org.eclipse.jgit.lib.SignatureVerifierFactory;
+
+/**
+ * Factory creating {@link SshSignatureVerifier}s.
+ *
+ * @since 7.1
+ */
+public final class SshSignatureVerifierFactory
+ implements SignatureVerifierFactory {
+
+ @Override
+ public GpgFormat getType() {
+ return GpgFormat.SSH;
+ }
+
+ @Override
+ public SignatureVerifier create() {
+ return new SshSignatureVerifier();
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignerFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignerFactory.java
new file mode 100644
index 0000000000..5459b5360a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignerFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.signing.ssh;
+
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.Signer;
+import org.eclipse.jgit.internal.signing.ssh.SshSigner;
+import org.eclipse.jgit.lib.SignerFactory;
+
+/**
+ * Factory creating {@link SshSigner}s.
+ *
+ * @since 7.1
+ */
+public final class SshSignerFactory implements SignerFactory {
+
+ @Override
+ public GpgFormat getType() {
+ return GpgFormat.SSH;
+ }
+
+ @Override
+ public Signer create() {
+ return new SshSigner();
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/VerificationException.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/VerificationException.java
new file mode 100644
index 0000000000..cd77111813
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/VerificationException.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.signing.ssh;
+
+/**
+ * An exception giving details about a failed
+ * {@link SigningKeyDatabase#isAllowed(org.eclipse.jgit.lib.Repository, org.eclipse.jgit.lib.GpgConfig, java.security.PublicKey, String, org.eclipse.jgit.lib.PersonIdent)}
+ * validation.
+ *
+ * @since 7.1
+ */
+public class VerificationException extends Exception {
+
+ private static final long serialVersionUID = 313760495170326160L;
+
+ private final boolean expired;
+
+ private final String reason;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param expired
+ * whether the checked public key or certificate was expired
+ * @param reason
+ * describing the check failure
+ */
+ public VerificationException(boolean expired, String reason) {
+ this.expired = expired;
+ this.reason = reason;
+ }
+
+ @Override
+ public String getMessage() {
+ return reason;
+ }
+
+ /**
+ * Tells whether the check failed because the public key was expired.
+ *
+ * @return {@code true} if the check failed because the public key was
+ * expired, {@code false} otherwise
+ */
+ public boolean isExpired() {
+ return expired;
+ }
+
+ /**
+ * Retrieves the check failure reason.
+ *
+ * @return the reason description
+ */
+ public String getReason() {
+ return reason;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProviderFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProviderFactory.java
new file mode 100644
index 0000000000..0537300b24
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProviderFactory.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.transport.CredentialsProvider;
+
+/**
+ * Maintains a static singleton instance of a factory to create a
+ * {@link KeyPasswordProvider} from a {@link CredentialsProvider}.
+ *
+ * @since 7.1
+ */
+public final class KeyPasswordProviderFactory {
+
+ /**
+ * Creates a {@link KeyPasswordProvider} from a {@link CredentialsProvider}.
+ */
+ @FunctionalInterface
+ public interface KeyPasswordProviderCreator
+ extends Function<CredentialsProvider, KeyPasswordProvider> {
+ // Nothing
+ }
+
+ private static final KeyPasswordProviderCreator DEFAULT = IdentityPasswordProvider::new;
+
+ private static AtomicReference<KeyPasswordProviderCreator> INSTANCE = new AtomicReference<>(
+ DEFAULT);
+
+ private KeyPasswordProviderFactory() {
+ // No instantiation
+ }
+
+ /**
+ * Retrieves the currently set {@link KeyPasswordProviderCreator}.
+ *
+ * @return the {@link KeyPasswordProviderCreator}
+ */
+ @NonNull
+ public static KeyPasswordProviderCreator getInstance() {
+ return INSTANCE.get();
+ }
+
+ /**
+ * Sets a new {@link KeyPasswordProviderCreator}.
+ *
+ * @param provider
+ * to set; if {@code null}, sets a default provider.
+ * @return the previously set {@link KeyPasswordProviderCreator}
+ */
+ @NonNull
+ public static KeyPasswordProviderCreator setInstance(
+ KeyPasswordProviderCreator provider) {
+ if (provider == null) {
+ return INSTANCE.getAndSet(DEFAULT);
+ }
+ return INSTANCE.getAndSet(provider);
+ }
+}
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 2c3cbe55c9..4a2eb9c3dd 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2024 Thomas Wolf <twolf@apache.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
@@ -210,7 +210,7 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable {
home, sshDir);
KeyIdentityProvider defaultKeysProvider = toKeyIdentityProvider(
getDefaultKeys(sshDir));
- Supplier<KeyPasswordProvider> keyPasswordProvider = () -> createKeyPasswordProvider(
+ Supplier<KeyPasswordProvider> keyPasswordProvider = newKeyPasswordProvider(
credentialsProvider);
SshClient client = ClientBuilder.builder()
.factory(JGitSshClient::new)
@@ -574,12 +574,24 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable {
* @param provider
* the {@link CredentialsProvider} to delegate to for user
* interactions
- * @return a new {@link KeyPasswordProvider}
+ * @return a new {@link KeyPasswordProvider}, or {@code null} to use the
+ * global {@link KeyPasswordProviderFactory}
*/
- @NonNull
protected KeyPasswordProvider createKeyPasswordProvider(
CredentialsProvider provider) {
- return new IdentityPasswordProvider(provider);
+ return null;
+ }
+
+ private Supplier<KeyPasswordProvider> newKeyPasswordProvider(
+ CredentialsProvider credentials) {
+ return () -> {
+ KeyPasswordProvider provider = createKeyPasswordProvider(
+ credentials);
+ if (provider != null) {
+ return provider;
+ }
+ return KeyPasswordProviderFactory.getInstance().apply(credentials);
+ };
}
/**
diff --git a/org.eclipse.jgit.ssh.apache/src/sun/security/x509/README.md b/org.eclipse.jgit.ssh.apache/src/sun/security/x509/README.md
deleted file mode 100644
index a84ee37ffb..0000000000
--- a/org.eclipse.jgit.ssh.apache/src/sun/security/x509/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-This dummy package is used to fix the error
-"Missing requirement: net.i2p.crypto.eddsa 0.3.0 requires 'java.package; sun.security.x509 0.0.0'"
-raised since eddsa falsely requires this import \ No newline at end of file
diff --git a/org.eclipse.jgit.ssh.jsch.test/BUILD b/org.eclipse.jgit.ssh.jsch.test/BUILD
index 4a8b92518e..d4e6875ed8 100644
--- a/org.eclipse.jgit.ssh.jsch.test/BUILD
+++ b/org.eclipse.jgit.ssh.jsch.test/BUILD
@@ -8,7 +8,9 @@ junit_tests(
srcs = glob(["tst/**/*.java"]),
tags = ["jsch"],
deps = [
- "//lib:eddsa",
+ "//lib:bcpkix",
+ "//lib:bcprov",
+ "//lib:bcutil",
"//lib:jsch",
"//lib:junit",
"//org.eclipse.jgit:jgit",
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 3bf4edd65f..34f8e0a126 100644
--- a/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
@@ -3,19 +3,20 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.ssh.jsch.test
Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
Require-Bundle: org.hamcrest.core;bundle-version="[1.3.0,2.0.0)"
Import-Package: com.jcraft.jsch;version="[0.1.54,0.2.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit.ssh;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.ssh.jsch;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit.ssh;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.ssh.jsch;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.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)"
diff --git a/org.eclipse.jgit.ssh.jsch.test/pom.xml b/org.eclipse.jgit.ssh.jsch.test/pom.xml
index 761b6c4aef..5f094a7812 100644
--- a/org.eclipse.jgit.ssh.jsch.test/pom.xml
+++ b/org.eclipse.jgit.ssh.jsch.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ssh.jsch.test</artifactId>
diff --git a/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/JSchSshProtocol2Test.java b/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/JSchSshProtocol2Test.java
index 611d4e8bcb..8aa33e3235 100644
--- a/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/JSchSshProtocol2Test.java
+++ b/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/JSchSshProtocol2Test.java
@@ -22,7 +22,6 @@ 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.CredentialsProvider;
import org.eclipse.jgit.transport.RemoteSession;
@@ -89,7 +88,8 @@ public class JSchSshProtocol2Test extends SshBasicTestBase {
@Override
public void setUp() throws Exception {
super.setUp();
- StoredConfig config = ((Repository) db).getConfig();
+ @SuppressWarnings("restriction")
+ StoredConfig config = db.getConfig();
config.setInt("protocol", null, "version", 2);
config.save();
}
diff --git a/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
index 9da7452428..6b0130ca52 100644
--- a/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
@@ -3,19 +3,19 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.ssh.jsch
Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch;singleton:=true
-Fragment-Host: org.eclipse.jgit;bundle-version="[7.0.0,7.1.0)"
+Fragment-Host: org.eclipse.jgit;bundle-version="[7.3.0,7.4.0)"
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: OSGI-INF/l10n/jsch
Bundle-ActivationPolicy: lazy
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="7.0.0"
+Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="7.3.0"
Import-Package: com.jcraft.jsch;version="[0.1.37,0.2.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.io;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.io;version="[7.3.0,7.4.0)",
org.slf4j;version="[1.7.0,3.0.0)"
diff --git a/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF
index 1dc0e01964..06d0ce3a14 100644
--- a/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.ssh.jsch - Sources
Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.jsch/pom.xml b/org.eclipse.jgit.ssh.jsch/pom.xml
index a3db63ca23..03ae29d8fa 100644
--- a/org.eclipse.jgit.ssh.jsch/pom.xml
+++ b/org.eclipse.jgit.ssh.jsch/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ssh.jsch</artifactId>
diff --git a/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/JschSession.java b/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/JschSession.java
index 5f36dadc04..ad58ae1c8e 100644
--- a/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/JschSession.java
+++ b/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/JschSession.java
@@ -34,7 +34,6 @@ import org.eclipse.jgit.transport.RemoteSession2;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.io.IsolatedOutputStream;
-import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
@@ -86,22 +85,6 @@ public class JschSession implements RemoteSession2 {
}
/**
- * A kludge to allow {@link org.eclipse.jgit.transport.TransportSftp} to get
- * an Sftp channel from Jsch. Ideally, this method would be generic, which
- * would require implementing generic Sftp channel operations in the
- * RemoteSession class.
- *
- * @return a channel suitable for Sftp operations.
- * @throws com.jcraft.jsch.JSchException
- * on problems getting the channel.
- * @deprecated since 5.2; use {@link #getFtpChannel()} instead
- */
- @Deprecated
- public Channel getSftpChannel() throws JSchException {
- return sock.openChannel("sftp"); //$NON-NLS-1$
- }
-
- /**
* {@inheritDoc}
*
* @since 5.2
diff --git a/org.eclipse.jgit.test/BUILD b/org.eclipse.jgit.test/BUILD
index 29f5b36149..7755df0499 100644
--- a/org.eclipse.jgit.test/BUILD
+++ b/org.eclipse.jgit.test/BUILD
@@ -53,6 +53,11 @@ tests(tests = glob(
exclude = HELPERS + DATA + EXCLUDED,
))
+
+tests(tests = glob(["exttst/**/*.java"]),
+ srcprefix = "exttst/",
+ extra_tags = ["ext"])
+
# Non abstract base classes used for tests by other test classes
BASE = [
PKG + "internal/storage/file/FileRepositoryBuilderTest.java",
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index ab70d68a3f..7ac93c2af2 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.test
Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -20,65 +20,68 @@ Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
org.apache.commons.compress.compressors.xz;version="[1.15.0,2.0)",
org.apache.commons.io;version="[2.15.0,3.0.0)",
org.apache.commons.io.output;version="[2.15.0,3.0.0)",
+ org.apache.commons.lang3;version="[3.17.0,4.0.0)",
org.assertj.core.api;version="[3.14.0,4.0.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.archive;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.attributes;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.awtui;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.blame;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.diff;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.dircache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.events;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.fnmatch;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.gitrepo;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.hooks;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.ignore;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.ignore.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.diff;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.fsck;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.commitgraph;version="7.0.0",
- org.eclipse.jgit.internal.storage.dfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.io;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.memory;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.pack;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.connectivity;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.parser;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit.time;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.logging;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.merge;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.notes;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.patch;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.pgm;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.pgm.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revplot;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.pack;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.submodule;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.resolver;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.io;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.sha1;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.archive;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.attributes;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.awtui;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.blame;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.blame.cache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.diff;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.dircache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.events;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.fnmatch;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.gitrepo;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.hooks;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.ignore;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.ignore.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.diff;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.fsck;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.commitgraph;version="7.3.0",
+ org.eclipse.jgit.internal.storage.dfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.io;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.memory;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.midx;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.connectivity;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit.time;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.logging;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.merge;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.notes;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.patch;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.pgm;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.pgm.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revplot;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.pack;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.submodule;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.io;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.sha1;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.experimental.theories;version="[4.13,5.0.0)",
org.junit.function;version="[4.13.0,5.0.0)",
diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/internal/storage/midx/CgitMidxCompatibilityTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/internal/storage/midx/CgitMidxCompatibilityTest.java
new file mode 100644
index 0000000000..88f0806c62
--- /dev/null
+++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/internal/storage/midx/CgitMidxCompatibilityTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * 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.midx;
+
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.CHUNK_LOOKUP_WIDTH;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OBJECTOFFSETS;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OIDFANOUT;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OIDLOOKUP;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_PACKNAMES;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.file.Pack;
+import org.eclipse.jgit.internal.storage.file.PackFile;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.util.NB;
+import org.junit.Test;
+
+public class CgitMidxCompatibilityTest extends SampleDataRepositoryTestCase {
+
+ @Test
+ public void jgitMidx_verifyByCgit()
+ throws IOException, InterruptedException {
+ byte[] jgitMidxBytes = generateJGitMidx();
+ writeMidx(jgitMidxBytes);
+ assertEquals("cgit exit code", 0, run_cgit_multipackindex_verify());
+ }
+
+ @Test
+ public void compareBasicChunkSizes()
+ throws IOException, InterruptedException {
+ // We cannot compare byte-by-byte because there are optional chunks and
+ // it is not guaranteed what cgit and jgit will generate
+ byte[] jgitMidxBytes = generateJGitMidx();
+ assertEquals("cgit exit code", 0, run_cgit_multipackindex_write());
+ byte[] cgitMidxBytes = readCgitMidx();
+
+ RawMultiPackIndex jgitMidx = new RawMultiPackIndex(jgitMidxBytes);
+ RawMultiPackIndex cgitMidx = new RawMultiPackIndex(cgitMidxBytes);
+
+ // This is a fixed sized chunk
+ assertEquals(256 * 4, cgitMidx.getChunkSize(MIDX_CHUNKID_OIDFANOUT));
+ assertArrayEquals(cgitMidx.getRawChunk(MIDX_CHUNKID_OIDFANOUT),
+ jgitMidx.getRawChunk(MIDX_CHUNKID_OIDFANOUT));
+
+ assertArrayEquals(cgitMidx.getRawChunk(MIDX_CHUNKID_OIDLOOKUP),
+ jgitMidx.getRawChunk(MIDX_CHUNKID_OIDLOOKUP));
+
+ // The spec has changed from padding packnames to a multile of four, to
+ // move the packname chunk to the end of the file.
+ // git 2.48 pads the packs names to a multiple of 4
+ // jgit puts the chunk at the end
+ byte[] cgitPacknames = trimPadding(
+ cgitMidx.getRawChunk(MIDX_CHUNKID_PACKNAMES));
+ assertArrayEquals(cgitPacknames,
+ jgitMidx.getRawChunk(MIDX_CHUNKID_PACKNAMES));
+
+ assertArrayEquals(cgitMidx.getRawChunk(MIDX_CHUNKID_OBJECTOFFSETS),
+ jgitMidx.getRawChunk(MIDX_CHUNKID_OBJECTOFFSETS));
+
+ }
+
+ private byte[] generateJGitMidx() throws IOException {
+ Map<String, PackIndex> indexes = new HashMap<>();
+ for (Pack pack : db.getObjectDatabase().getPacks()) {
+ PackFile packFile = pack.getPackFile().create(PackExt.INDEX);
+ indexes.put(packFile.getName(), pack.getIndex());
+ }
+
+ MultiPackIndexWriter writer = new MultiPackIndexWriter();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writer.write(NullProgressMonitor.INSTANCE, out, indexes);
+ return out.toByteArray();
+ }
+
+ private int run_cgit_multipackindex_write()
+ throws IOException, InterruptedException {
+ String[] command = new String[] { "git", "multi-pack-index", "write" };
+ Process proc = Runtime.getRuntime().exec(command, new String[0],
+ db.getDirectory());
+ return proc.waitFor();
+ }
+
+ private int run_cgit_multipackindex_verify()
+ throws IOException, InterruptedException {
+ String[] command = new String[] { "git", "multi-pack-index", "verify" };
+ Process proc = Runtime.getRuntime().exec(command, new String[0],
+ db.getDirectory());
+ return proc.waitFor();
+ }
+
+ private byte[] readCgitMidx() throws IOException {
+ File midx = getMIdxStandardLocation();
+ assertTrue("cgit multi-pack-index exists", midx.exists());
+ return Files.readAllBytes(midx.toPath());
+ }
+
+ private void writeMidx(byte[] midx) throws IOException {
+ File midxFile = getMIdxStandardLocation();
+ Files.write(midxFile.toPath(), midx);
+ }
+
+ private File getMIdxStandardLocation() {
+ return new File(db.getObjectDatabase().getPackDirectory(),
+ "multi-pack-index");
+ }
+
+ private byte[] trimPadding(byte[] data) {
+ // Chunk MUST have one \0, we want to remove any extra \0
+ int newEnd = data.length - 1;
+ while (newEnd - 1 >= 0 && data[newEnd - 1] == 0) {
+ newEnd--;
+ }
+
+ if (newEnd == data.length - 1) {
+ return data;
+ }
+ return Arrays.copyOfRange(data, 0, newEnd + 1);
+ }
+
+ private static class RawMultiPackIndex {
+ private final List<ChunkSegment> chunks;
+
+ private final byte[] midx;
+
+ private RawMultiPackIndex(byte[] midx) {
+ this.chunks = readChunks(midx);
+ this.midx = midx;
+ }
+
+ long getChunkSize(int chunkId) {
+ int chunkPos = findChunkPosition(chunks, chunkId);
+ return chunks.get(chunkPos + 1).offset
+ - chunks.get(chunkPos).offset;
+ }
+
+ long getOffset(int chunkId) {
+ return chunks.get(findChunkPosition(chunks, chunkId)).offset;
+ }
+
+ private long getNextOffset(int chunkId) {
+ return chunks.get(findChunkPosition(chunks, chunkId) + 1).offset;
+ }
+
+ byte[] getRawChunk(int chunkId) {
+ int start = (int) getOffset(chunkId);
+ int end = (int) getNextOffset(chunkId);
+ return Arrays.copyOfRange(midx, start, end);
+ }
+
+ private static int findChunkPosition(List<ChunkSegment> chunks,
+ int id) {
+ int chunkPos = -1;
+ for (int i = 0; i < chunks.size(); i++) {
+ if (chunks.get(i).id() == id) {
+ chunkPos = i;
+ break;
+ }
+ }
+ if (chunkPos == -1) {
+ throw new IllegalStateException("Chunk doesn't exist");
+ }
+ return chunkPos;
+ }
+
+ private List<ChunkSegment> readChunks(byte[] midx) {
+ // Read the number of "chunkOffsets" (1 byte)
+ int chunkCount = midx[6];
+ byte[] lookupBuffer = new byte[CHUNK_LOOKUP_WIDTH
+ * (chunkCount + 1)];
+ System.arraycopy(midx, 12, lookupBuffer, 0, lookupBuffer.length);
+
+ List<ChunkSegment> chunks = new ArrayList<>(chunkCount + 1);
+ for (int i = 0; i <= chunkCount; i++) {
+ // chunks[chunkCount] is just a marker, in order to record the
+ // length of the last chunk.
+ int id = NB.decodeInt32(lookupBuffer, i * 12);
+ long offset = NB.decodeInt64(lookupBuffer, i * 12 + 4);
+ chunks.add(new ChunkSegment(id, offset));
+ }
+ return chunks;
+ }
+ }
+
+ private record ChunkSegment(int id, long offset) {
+ }
+}
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index 05f7c08bd2..9cf21fd778 100644
--- a/org.eclipse.jgit.test/pom.xml
+++ b/org.eclipse.jgit.test/pom.xml
@@ -19,7 +19,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.test</artifactId>
diff --git a/org.eclipse.jgit.test/tests.bzl b/org.eclipse.jgit.test/tests.bzl
index 170bf0c460..41f76d090a 100644
--- a/org.eclipse.jgit.test/tests.bzl
+++ b/org.eclipse.jgit.test/tests.bzl
@@ -1,11 +1,29 @@
+'''
+Expose each test as a bazel target
+'''
load(
"@com_googlesource_gerrit_bazlets//tools:junit.bzl",
"junit_tests",
)
-def tests(tests):
+def tests(tests, srcprefix="tst/", extra_tags=[]):
+ '''
+ Create a target each of the tests
+
+ Each target is the full push (removing srcprefix) replacing directory
+ separators with underscores.
+
+ e.g. a test under tst/a/b/c/A.test will become the target
+ //org.eclipse.jgit.tests:a_b_c_A
+
+ Args:
+ tests: a glob of tests files
+ srcprefix: prefix between org.eclipse.jgit.tests and the package
+ start
+ extra_tags: additional tags to add to the generated targets
+ '''
for src in tests:
- name = src[len("tst/"):len(src) - len(".java")].replace("/", "_")
+ name = src[len(srcprefix):len(src) - len(".java")].replace("/", "_")
labels = []
timeout = "moderate"
if name.startswith("org_eclipse_jgit_"):
@@ -20,6 +38,8 @@ def tests(tests):
if "lib" not in labels:
labels.append("lib")
+ labels.extend(extra_tags)
+
# TODO(http://eclip.se/534285): Make this test pass reliably
# and remove the flaky attribute.
flaky = src.endswith("CrissCrossMergeTest.java")
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds.patch
new file mode 100644
index 0000000000..6e7448b95c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds.patch
@@ -0,0 +1,10 @@
+diff --git a/ConflictOutOfBounds b/ConflictOutOfBounds
+index 0000000..de98044
+--- a/ConflictOutOfBounds
++++ b/ConflictOutOfBounds
+@@ -25,4 +25,4 @@
+ line3
+-lineA
++lineB
+ line5
+ line6
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PostImage
new file mode 100644
index 0000000000..4e5d5b2d88
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PostImage
@@ -0,0 +1,15 @@
+line1
+line2
+line3
+line4
+line5
+line6
+line7
+line8
+<<<<<<< HEAD
+=======
+line3
+lineB
+line5
+line6
+>>>>>>> PATCH
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PreImage
new file mode 100644
index 0000000000..f62562a216
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PreImage
@@ -0,0 +1,8 @@
+line1
+line2
+line3
+line4
+line5
+line6
+line7
+line8
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict.patch
new file mode 100644
index 0000000000..a99e636382
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict.patch
@@ -0,0 +1,10 @@
+diff --git a/allowconflict b/allowconflict
+index 0000000..de98044
+--- a/allowconflict
++++ b/allowconflict
+@@ -3,4 +3,4 @@
+ line3
+-lineA
++lineB
+ line5
+ line6
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PostImage
new file mode 100644
index 0000000000..a963b40dfe
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PostImage
@@ -0,0 +1,15 @@
+line1
+line2
+<<<<<<< HEAD
+line3
+line4
+line5
+line6
+=======
+line3
+lineB
+line5
+line6
+>>>>>>> PATCH
+line7
+line8
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PreImage
new file mode 100644
index 0000000000..f62562a216
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PreImage
@@ -0,0 +1,8 @@
+line1
+line2
+line3
+line4
+line5
+line6
+line7
+line8
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_file_deleted.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_file_deleted.patch
new file mode 100644
index 0000000000..c9655a5e6b
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_file_deleted.patch
@@ -0,0 +1,10 @@
+diff --git a/allowconflict_file_deleted b/allowconflict_file_deleted
+index 0000000..de98044
+--- a/allowconflict_file_deleted
++++ b/allowconflict_file_deleted
+@@ -3,4 +3,4 @@
+ line3
+-lineA
++lineB
+ line5
+ line6
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.c b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.c
new file mode 100644
index 0000000000..3661160921
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+void getGreeting(char *result, const char *name) {
+ sprintf(result, "Hello, %s!", name);
+}
+
+void getFarewell(char *result, const char *name) {
+ sprintf(result, "Goodbye, %s. Have a great day!", name);
+}
+
+void toLower(char *str) {
+ for (int i = 0; str[i]; i++) {
+ str[i] = tolower(str[i]);
+ }
+}
+
+void getPersonalizedGreeting(char *result, const char *name, const char *timeOfDay) {
+ char timeOfDayLower[50];
+ strcpy(timeOfDayLower, timeOfDay);
+ toLower(timeOfDayLower);
+ if (strcmp(timeOfDayLower, "morning") == 0) {
+ sprintf(result, "Good morning, %s", name);
+ } else if (strcmp(timeOfDayLower, "afternoon") == 0) {
+ sprintf(result, "Good afternoon, %s", name);
+ } else if (strcmp(timeOfDayLower, "evening") == 0) {
+ sprintf(result, "Good evening, %s", name);
+ } else {
+ sprintf(result, "Good day, %s", name);
+ }
+}
+
+int main() {
+ char result[100];
+ getGreeting(result, "foo");
+ printf("%s\\n", result);
+ getFarewell(result, "bar");
+ printf("%s\\n", result);
+ getPersonalizedGreeting(result, "baz", "morning");
+ printf("%s\\n", result);
+ return 0;
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.javasource b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.javasource
new file mode 100644
index 0000000000..9659685c63
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.javasource
@@ -0,0 +1,37 @@
+public class Greeting {
+ public String getGreeting(String name) {
+ String msg = "Hello, " + name + "!";
+ return msg;
+ }
+
+ public String getFarewell(String name) {
+ String msg = "Goodbye, " + name + ". Have a great day!";
+ return msg;
+ }
+
+ public String getPersonalizedGreeting(String name, String timeOfDay) {
+ String msg;
+ switch (timeOfDay.toLowerCase()) {
+ case "morning":
+ msg = "Good morning, " + name;
+ break;
+ case "afternoon":
+ msg = "Good afternoon, " + name;
+ break;
+ case "evening":
+ msg = "Good evening, " + name;
+ break;
+ default:
+ msg = "Good day, " + name;
+ break;
+ }
+ return msg;
+ }
+
+ public static void main(String[] args) {
+ Greeting greeting = new Greeting();
+ System.out.println(greeting.getGreeting("foo"));
+ System.out.println(greeting.getFarewell("bar"));
+ System.out.println(greeting.getPersonalizedGreeting("baz", "morning"));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.py b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.py
new file mode 100644
index 0000000000..9eda6cd8fe
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.py
@@ -0,0 +1,26 @@
+class Greeting:
+ def get_greeting(self, name):
+ greeting_message = f"Hello, {name}!"
+ return greeting_message
+
+ def get_farewell(self, name):
+ farewell_message = f"Goodbye, {name}. Have a great day!"
+ return farewell_message
+
+ def get_personalized_greeting(self, name, time_of_day):
+ time_of_day = time_of_day.lower()
+ if time_of_day == "morning":
+ personalized_message = f"Good morning, {name}"
+ elif time_of_day == "afternoon":
+ personalized_message = f"Good afternoon, {name}"
+ elif time_of_day == "evening":
+ personalized_message = f"Good evening, {name}"
+ else:
+ personalized_message = f"Good day, {name}"
+ return personalized_message
+
+if __name__ == "__main__":
+ greeting = Greeting()
+ print(greeting.get_greeting("foo"))
+ print(greeting.get_farewell("bar"))
+ print(greeting.get_personalized_greeting("baz", "morning"))
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.rs b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.rs
new file mode 100644
index 0000000000..a3aa5cbe7c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.rs
@@ -0,0 +1,27 @@
+struct Greeting;
+
+impl Greeting {
+ fn get_greeting(&self, name: &str) -> String {
+ format!("Hello, {}!", name)
+ }
+
+ fn get_farewell(&self, name: &str) -> String {
+ format!("Goodbye, {}. Have a great day!", name)
+ }
+
+ fn get_personalized_greeting(&self, name: &str, time_of_day: &str) -> String {
+ match time_of_day.to_lowercase().as_str() {
+ "morning" => format!("Good morning, {}", name),
+ "afternoon" => format!("Good afternoon, {}", name),
+ "evening" => format!("Good evening, {}", name),
+ _ => format!("Good day, {}", name),
+ }
+ }
+}
+
+fn main() {
+ let greeting = Greeting;
+ println!("{}", greeting.get_greeting("foo"));
+ println!("{}", greeting.get_farewell("bar"));
+ println!("{}", greeting.get_personalized_greeting("baz", "morning"));
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/sample.dtsi b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/sample.dtsi
new file mode 100644
index 0000000000..6aa4ecdd4c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/sample.dtsi
@@ -0,0 +1,25 @@
+/dts-v1/;
+
+/ {
+ model = "Example Board";
+ compatible = "example,board";
+ cpus {
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <0>;
+ };
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x80000000 0x20000000>;
+ };
+
+ uart0: uart@101f1000 {
+ compatible = "ns16550a";
+ reg = <0x101f1000 0x1000>;
+ interrupts = <5>;
+ clock-frequency = <24000000>;
+ };
+};
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index 1c2e995bbb..226677229c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com>
- * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> and others
+ * Copyright (C) 2010, 2025 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
@@ -665,11 +665,13 @@ public class AddCommandTest extends RepositoryTestCase {
FileUtils.delete(file);
// is supposed to do nothing
- dc = git.add().addFilepattern("a.txt").call();
+ dc = git.add().addFilepattern("a.txt").setAll(false).call();
assertEquals(oid, dc.getEntry(0).getObjectId());
assertEquals(
"[a.txt, mode:100644, content:content]",
indexState(CONTENT));
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("", indexState(CONTENT));
}
}
@@ -690,11 +692,13 @@ public class AddCommandTest extends RepositoryTestCase {
FileUtils.delete(file);
// is supposed to do nothing
- dc = git.add().addFilepattern("a.txt").call();
+ dc = git.add().addFilepattern("a.txt").setAll(false).call();
assertEquals(oid, dc.getEntry(0).getObjectId());
assertEquals(
"[a.txt, mode:100644, content:content]",
indexState(CONTENT));
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("", indexState(CONTENT));
}
}
@@ -964,7 +968,7 @@ public class AddCommandTest extends RepositoryTestCase {
// file sub/b.txt is deleted
FileUtils.delete(file2);
- git.add().addFilepattern("sub").call();
+ git.add().addFilepattern("sub").setAll(false).call();
// change in sub/a.txt is staged
// deletion of sub/b.txt is not staged
// sub/c.txt is staged
@@ -973,6 +977,12 @@ public class AddCommandTest extends RepositoryTestCase {
"[sub/b.txt, mode:100644, content:content b]" +
"[sub/c.txt, mode:100644, content:content c]",
indexState(CONTENT));
+ git.add().addFilepattern("sub").call();
+ // deletion of sub/b.txt is staged
+ assertEquals(
+ "[sub/a.txt, mode:100644, content:modified content]"
+ + "[sub/c.txt, mode:100644, content:content c]",
+ indexState(CONTENT));
}
}
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 3a4ea8e733..9c2b16a0ae 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
@@ -267,7 +267,10 @@ public class ArchiveCommandTest extends RepositoryTestCase {
archive(git, archive, fmt, Map.of("compression-level", 9));
int sizeCompression9 = getNumBytes(archive);
- assertTrue(sizeCompression1 > sizeCompression9);
+ assertTrue(
+ "Expected sizeCompression1 = " + sizeCompression1
+ + " > sizeCompression9 = " + sizeCompression9,
+ sizeCompression1 > sizeCompression9);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
index be3b33a9c5..3f5c5da55a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
@@ -34,6 +34,7 @@ import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.merge.ContentMergeStrategy;
@@ -529,10 +530,11 @@ public class CherryPickCommandTest extends RepositoryTestCase {
assertEquals(RepositoryState.SAFE, db.getRepositoryState());
if (reason == null) {
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
assertTrue(reader.getLastEntry().getComment()
.startsWith("cherry-pick: "));
- reader = db.getReflogReader(db.getBranch());
+ reader = refDb.getReflogReader(db.getFullBranch());
assertTrue(reader.getLastEntry().getComment()
.startsWith("cherry-pick: "));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
index 63ab8094ae..661878fa07 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
@@ -182,7 +182,8 @@ public class CloneCommandTest extends RepositoryTestCase {
private static boolean hasRefLog(Repository repo, Ref ref) {
try {
- return repo.getReflogReader(ref.getName()).getLastEntry() != null;
+ return repo.getRefDatabase().getReflogReader(ref)
+ .getLastEntry() != null;
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
@@ -647,7 +648,8 @@ public class CloneCommandTest extends RepositoryTestCase {
new File(git.getRepository().getWorkTree(), walk.getPath()),
subRepo.getWorkTree());
assertEquals(new File(new File(git.getRepository().getDirectory(),
- "modules"), walk.getPath()), subRepo.getDirectory());
+ "modules"), walk.getPath()).getCanonicalPath(),
+ subRepo.getDirectory().getCanonicalPath());
}
File directory = createTempDirectory("testCloneRepositoryWithSubmodules");
@@ -681,8 +683,8 @@ public class CloneCommandTest extends RepositoryTestCase {
walk.getPath()), clonedSub1.getWorkTree());
assertEquals(
new File(new File(git2.getRepository().getDirectory(),
- "modules"), walk.getPath()),
- clonedSub1.getDirectory());
+ "modules"), walk.getPath()).getCanonicalPath(),
+ clonedSub1.getDirectory().getCanonicalPath());
}
}
@@ -770,8 +772,8 @@ public class CloneCommandTest extends RepositoryTestCase {
walk.getPath()), clonedSub1.getWorkTree());
assertEquals(
new File(new File(git2.getRepository().getDirectory(),
- "modules"), walk.getPath()),
- clonedSub1.getDirectory());
+ "modules"), walk.getPath()).getCanonicalPath(),
+ clonedSub1.getDirectory().getCanonicalPath());
status = new SubmoduleStatusCommand(clonedSub1);
statuses = status.call();
}
@@ -795,7 +797,7 @@ public class CloneCommandTest extends RepositoryTestCase {
assertNull(git2.getRepository().getConfig().getEnum(
BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, "test",
- ConfigConstants.CONFIG_KEY_REBASE, null));
+ ConfigConstants.CONFIG_KEY_REBASE));
StoredConfig userConfig = SystemReader.getInstance()
.getUserConfig();
@@ -811,7 +813,6 @@ public class CloneCommandTest extends RepositoryTestCase {
addRepoToClose(git2.getRepository());
assertEquals(BranchRebaseMode.REBASE,
git2.getRepository().getConfig().getEnum(
- BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, "test",
ConfigConstants.CONFIG_KEY_REBASE,
BranchRebaseMode.NONE));
@@ -828,7 +829,6 @@ public class CloneCommandTest extends RepositoryTestCase {
addRepoToClose(git2.getRepository());
assertEquals(BranchRebaseMode.REBASE,
git2.getRepository().getConfig().getEnum(
- BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, "test",
ConfigConstants.CONFIG_KEY_REBASE,
BranchRebaseMode.NONE));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
index 57e5d4958f..4e5f44e5a6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
@@ -26,6 +26,7 @@ import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -69,10 +70,11 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
l--;
}
assertEquals(l, -1);
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
assertTrue(
reader.getLastEntry().getComment().startsWith("commit:"));
- reader = db.getReflogReader(db.getBranch());
+ reader = refDb.getReflogReader(db.getFullBranch());
assertTrue(
reader.getLastEntry().getComment().startsWith("commit:"));
}
@@ -248,10 +250,11 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
c++;
}
assertEquals(1, c);
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
assertTrue(reader.getLastEntry().getComment()
.startsWith("commit (amend):"));
- reader = db.getReflogReader(db.getBranch());
+ reader = refDb.getReflogReader(db.getFullBranch());
assertTrue(reader.getLastEntry().getComment()
.startsWith("commit (amend):"));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
index 35de73e204..21cfcc4e34 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
@@ -19,14 +19,15 @@ import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import java.io.File;
-import java.util.Date;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.List;
-import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jgit.api.CherryPickResult.CherryPickStatus;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.api.errors.EmptyCommitException;
+import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.dircache.DirCache;
@@ -34,19 +35,23 @@ import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.junit.time.TimeUtil;
-import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.CommitConfig.CleanupMode;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.GpgSigner;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.GpgSignature;
+import org.eclipse.jgit.lib.ObjectBuilder;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Signer;
+import org.eclipse.jgit.lib.Signers;
import org.eclipse.jgit.lib.StoredConfig;
-import org.eclipse.jgit.lib.CommitConfig.CleanupMode;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.submodule.SubmoduleWalk;
@@ -430,10 +435,12 @@ public class CommitCommandTest extends RepositoryTestCase {
assertEquals(1, squashedCommit.getParentCount());
assertNull(db.readSquashCommitMsg());
- assertEquals("commit: Squashed commit of the following:", db
- .getReflogReader(Constants.HEAD).getLastEntry().getComment());
- assertEquals("commit: Squashed commit of the following:", db
- .getReflogReader(db.getBranch()).getLastEntry().getComment());
+ assertEquals("commit: Squashed commit of the following:",
+ db.getRefDatabase().getReflogReader(Constants.HEAD)
+ .getLastEntry().getComment());
+ assertEquals("commit: Squashed commit of the following:",
+ db.getRefDatabase().getReflogReader(db.getFullBranch())
+ .getLastEntry().getComment());
}
}
@@ -450,12 +457,15 @@ public class CommitCommandTest extends RepositoryTestCase {
git.commit().setMessage("c3").setAll(true)
.setReflogComment("testRl").call();
- db.getReflogReader(Constants.HEAD).getReverseEntries();
+ db.getRefDatabase().getReflogReader(Constants.HEAD)
+ .getReverseEntries();
assertEquals("testRl;commit (initial): c1;", reflogComments(
- db.getReflogReader(Constants.HEAD).getReverseEntries()));
+ db.getRefDatabase().getReflogReader(Constants.HEAD)
+ .getReverseEntries()));
assertEquals("testRl;commit (initial): c1;", reflogComments(
- db.getReflogReader(db.getBranch()).getReverseEntries()));
+ db.getRefDatabase().getReflogReader(db.getFullBranch())
+ .getReverseEntries()));
}
}
@@ -481,11 +491,11 @@ public class CommitCommandTest extends RepositoryTestCase {
writeTrashFile("file1", "file1");
git.add().addFilepattern("file1").call();
- final String authorName = "First Author";
- final String authorEmail = "author@example.org";
- final Date authorDate = new Date(1349621117000L);
+ String authorName = "First Author";
+ String authorEmail = "author@example.org";
+ Instant authorDate = Instant.ofEpochSecond(1349621117L);
PersonIdent firstAuthor = new PersonIdent(authorName, authorEmail,
- authorDate, TimeZone.getTimeZone("UTC"));
+ authorDate, ZoneOffset.UTC);
git.commit().setMessage("initial commit").setAuthor(firstAuthor).call();
RevCommit amended = git.commit().setAmend(true)
@@ -494,7 +504,8 @@ public class CommitCommandTest extends RepositoryTestCase {
PersonIdent amendedAuthor = amended.getAuthorIdent();
assertEquals(authorName, amendedAuthor.getName());
assertEquals(authorEmail, amendedAuthor.getEmailAddress());
- assertEquals(authorDate.getTime(), amendedAuthor.getWhen().getTime());
+ assertEquals(authorDate.getEpochSecond(),
+ amendedAuthor.getWhenAsInstant().getEpochSecond());
}
}
@@ -839,21 +850,39 @@ public class CommitCommandTest extends RepositoryTestCase {
String[] signingKey = new String[1];
PersonIdent[] signingCommitters = new PersonIdent[1];
AtomicInteger callCount = new AtomicInteger();
- GpgSigner.setDefault(new GpgSigner() {
+ // Since GpgFormat defaults to OpenPGP just set a new signer for
+ // that.
+ Signers.set(GpgFormat.OPENPGP, new Signer() {
+
@Override
- public void sign(CommitBuilder commit, String gpgSigningKey,
- PersonIdent signingCommitter, CredentialsProvider credentialsProvider) {
- signingKey[0] = gpgSigningKey;
+ public void signObject(Repository repo, GpgConfig config,
+ ObjectBuilder builder, PersonIdent signingCommitter,
+ String signingKeySpec,
+ CredentialsProvider credentialsProvider)
+ throws CanceledException,
+ UnsupportedSigningFormatException {
+ signingKey[0] = signingKeySpec;
signingCommitters[0] = signingCommitter;
callCount.incrementAndGet();
}
@Override
- public boolean canLocateSigningKey(String gpgSigningKey,
- PersonIdent signingCommitter,
+ public GpgSignature sign(Repository repo, GpgConfig config,
+ byte[] data, PersonIdent signingCommitter,
+ String signingKeySpec,
+ CredentialsProvider credentialsProvider)
+ throws CanceledException,
+ UnsupportedSigningFormatException {
+ throw new CanceledException("Unexpected call");
+ }
+
+ @Override
+ public boolean canLocateSigningKey(Repository repo,
+ GpgConfig config, PersonIdent signingCommitter,
+ String signingKeySpec,
CredentialsProvider credentialsProvider)
throws CanceledException {
- return false;
+ throw new CanceledException("Unexpected call");
}
});
@@ -904,19 +933,37 @@ public class CommitCommandTest extends RepositoryTestCase {
git.add().addFilepattern("file1").call();
AtomicInteger callCount = new AtomicInteger();
- GpgSigner.setDefault(new GpgSigner() {
+ // Since GpgFormat defaults to OpenPGP just set a new signer for
+ // that.
+ Signers.set(GpgFormat.OPENPGP, new Signer() {
+
@Override
- public void sign(CommitBuilder commit, String gpgSigningKey,
- PersonIdent signingCommitter, CredentialsProvider credentialsProvider) {
+ public void signObject(Repository repo, GpgConfig config,
+ ObjectBuilder builder, PersonIdent signingCommitter,
+ String signingKeySpec,
+ CredentialsProvider credentialsProvider)
+ throws CanceledException,
+ UnsupportedSigningFormatException {
callCount.incrementAndGet();
}
@Override
- public boolean canLocateSigningKey(String gpgSigningKey,
- PersonIdent signingCommitter,
+ public GpgSignature sign(Repository repo, GpgConfig config,
+ byte[] data, PersonIdent signingCommitter,
+ String signingKeySpec,
+ CredentialsProvider credentialsProvider)
+ throws CanceledException,
+ UnsupportedSigningFormatException {
+ throw new CanceledException("Unexpected call");
+ }
+
+ @Override
+ public boolean canLocateSigningKey(Repository repo,
+ GpgConfig config, PersonIdent signingCommitter,
+ String signingKeySpec,
CredentialsProvider credentialsProvider)
throws CanceledException {
- return false;
+ throw new CanceledException("Unexpected call");
}
});
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
index ab87fa9662..060e6d3e84 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
@@ -12,6 +12,7 @@ package org.eclipse.jgit.api;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -87,6 +88,9 @@ public class DescribeCommandTest extends RepositoryTestCase {
assertEquals("alice-t1", describe(c2, "alice*"));
assertEquals("alice-t1", describe(c2, "a*", "b*", "c*"));
+ assertNotEquals("alice-t1", describeExcluding(c2, "alice*"));
+ assertNotEquals("alice-t1", describeCommand(c2).setMatch("*").setExclude("alice*").call());
+
assertEquals("bob-t2", describe(c3));
assertEquals("bob-t2-0-g44579eb", describe(c3, true, false));
assertEquals("alice-t1-1-g44579eb", describe(c3, "alice*"));
@@ -95,6 +99,15 @@ public class DescribeCommandTest extends RepositoryTestCase {
assertEquals("bob-t2", describe(c3, "?ob*"));
assertEquals("bob-t2", describe(c3, "a*", "b*", "c*"));
+ assertNotEquals("alice-t1-1-g44579eb", describeExcluding(c3, "alice*"));
+ assertNotEquals("alice-t1-1-g44579eb", describeCommand(c3).setMatch("*").setExclude("alice*").call());
+ assertNotEquals("alice-t1-1-g44579eb", describeExcluding(c3, "a??c?-t*"));
+ assertNotEquals("alice-t1-1-g44579eb", describeCommand(c3).setMatch("bob*").setExclude("a??c?-t*").call());
+ assertNotEquals("bob-t2", describeExcluding(c3, "bob*"));
+ assertNotEquals("bob-t2", describeCommand(c3).setMatch("alice*").setExclude("bob*"));
+ assertNotEquals("bob-t2", describeExcluding(c3, "?ob*"));
+ assertNotEquals("bob-t2", describeCommand(c3).setMatch("a??c?-t*").setExclude("?ob*"));
+
// the value verified with git-describe(1)
assertEquals("bob-t2-1-g3e563c5", describe(c4));
assertEquals("bob-t2-1-g3e563c5", describe(c4, true, false));
@@ -518,6 +531,15 @@ public class DescribeCommandTest extends RepositoryTestCase {
.setMatch(patterns).call();
}
+ private String describeExcluding(ObjectId c1, String... patterns) throws Exception {
+ return git.describe().setTarget(c1).setTags(describeUseAllTags)
+ .setExclude(patterns).call();
+ }
+
+ private DescribeCommand describeCommand(ObjectId c1) throws Exception {
+ return git.describe().setTarget(c1).setTags(describeUseAllTags);
+ }
+
private static void assertNameStartsWith(ObjectId c4, String prefix) {
assertTrue(c4.name(), c4.name().startsWith(prefix));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
index b937b1f6a9..4c971ffb6b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
@@ -559,7 +559,7 @@ public class EolRepositoryTest extends RepositoryTestCase {
}
if (infoAttributesContent != null) {
- File f = new File(db.getDirectory(), Constants.INFO_ATTRIBUTES);
+ File f = new File(db.getCommonDirectory(), Constants.INFO_ATTRIBUTES);
write(f, infoAttributesContent);
}
config.save();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java
index 3ec454cfc3..3731347f11 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java
@@ -92,8 +92,8 @@ public class FetchCommandTest extends RepositoryTestCase {
assertTrue(remoteRef.getName().startsWith(Constants.R_REMOTES));
assertEquals(defaultBranchSha1, remoteRef.getObjectId());
- assertNotNull(git.getRepository().getReflogReader(remoteRef.getName())
- .getLastEntry());
+ assertNotNull(git.getRepository().getRefDatabase()
+ .getReflogReader(remoteRef.getName()).getLastEntry());
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java
index f98db3497b..6090d5efbe 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java
@@ -11,12 +11,11 @@ package org.eclipse.jgit.api;
import static org.junit.Assert.assertTrue;
-import java.util.Date;
+import java.time.Instant;
import java.util.Properties;
import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.util.GitDateParser;
-import org.eclipse.jgit.util.SystemReader;
+import org.eclipse.jgit.util.GitTimeParser;
import org.junit.Before;
import org.junit.Test;
@@ -36,9 +35,8 @@ public class GarbageCollectCommandTest extends RepositoryTestCase {
@Test
public void testGConeCommit() throws Exception {
- Date expire = GitDateParser.parse("now", null, SystemReader
- .getInstance().getLocale());
- Properties res = git.gc().setExpire(expire).call();
+ Instant expireNow = GitTimeParser.parseInstant("now");
+ Properties res = git.gc().setExpire(expireNow).call();
assertTrue(res.size() == 8);
}
@@ -52,11 +50,8 @@ public class GarbageCollectCommandTest extends RepositoryTestCase {
writeTrashFile("b.txt", "a couple of words for gc to pack more 2");
writeTrashFile("c.txt", "a couple of words for gc to pack more 3");
git.commit().setAll(true).setMessage("commit3").call();
- Properties res = git
- .gc()
- .setExpire(
- GitDateParser.parse("now", null, SystemReader
- .getInstance().getLocale())).call();
+ Instant expireNow = GitTimeParser.parseInstant("now");
+ Properties res = git.gc().setExpire(expireNow).call();
assertTrue(res.size() == 8);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
index 76934343da..e847e72415 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
@@ -14,6 +14,7 @@ import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
+import java.time.Instant;
import org.eclipse.jgit.api.ListBranchCommand.ListMode;
import org.eclipse.jgit.api.errors.GitAPIException;
@@ -100,7 +101,7 @@ public class GitConstructionTest extends RepositoryTestCase {
GitAPIException {
File workTree = db.getWorkTree();
Git git = Git.open(workTree);
- git.gc().setExpire(null).call();
+ git.gc().setExpire((Instant) null).call();
git.checkout().setName(git.getRepository().resolve("HEAD^").getName())
.call();
try {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LinkedWorktreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LinkedWorktreeTest.java
new file mode 100644
index 0000000000..3b60e1b5c0
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LinkedWorktreeTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2024, Broadcom 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 static org.eclipse.jgit.lib.Constants.HEAD;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.ExecutionResult;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.TemporaryBuffer;
+import org.junit.Test;
+
+public class LinkedWorktreeTest extends RepositoryTestCase {
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("Initial commit").call();
+ }
+ }
+
+ @Test
+ public void testWeCanReadFromLinkedWorktreeFromBare() throws Exception {
+ FS fs = db.getFS();
+ File directory = trash.getParentFile();
+ String dbDirName = db.getWorkTree().getName();
+ cloneBare(fs, directory, dbDirName, "bare");
+ File bareDirectory = new File(directory, "bare");
+ worktreeAddExisting(fs, bareDirectory, "master");
+
+ File worktreesDir = new File(bareDirectory, "worktrees");
+ File masterWorktreesDir = new File(worktreesDir, "master");
+
+ FileRepository repository = new FileRepository(masterWorktreesDir);
+ try (Git git = new Git(repository)) {
+ ObjectId objectId = repository.resolve(HEAD);
+ assertNotNull(objectId);
+
+ Iterator<RevCommit> log = git.log().all().call().iterator();
+ assertTrue(log.hasNext());
+ assertTrue("Initial commit".equals(log.next().getShortMessage()));
+
+ // we have reflog entry
+ // depending on git version we either have one or
+ // two entries where extra is zeroid entry with
+ // same message or no message
+ Collection<ReflogEntry> reflog = git.reflog().call();
+ assertNotNull(reflog);
+ assertTrue(reflog.size() > 0);
+ ReflogEntry[] reflogs = reflog.toArray(new ReflogEntry[0]);
+ assertEquals(reflogs[reflogs.length - 1].getComment(),
+ "reset: moving to HEAD");
+
+ // index works with file changes
+ File masterDir = new File(directory, "master");
+ File testFile = new File(masterDir, "test");
+
+ Status status = git.status().call();
+ assertTrue(status.getUncommittedChanges().size() == 0);
+ assertTrue(status.getUntracked().size() == 0);
+
+ JGitTestUtil.write(testFile, "test");
+ status = git.status().call();
+ assertTrue(status.getUncommittedChanges().size() == 0);
+ assertTrue(status.getUntracked().size() == 1);
+
+ git.add().addFilepattern("test").call();
+ status = git.status().call();
+ assertTrue(status.getUncommittedChanges().size() == 1);
+ assertTrue(status.getUntracked().size() == 0);
+ }
+ }
+
+ @Test
+ public void testWeCanReadFromLinkedWorktreeFromNonBare() throws Exception {
+ FS fs = db.getFS();
+ worktreeAddNew(fs, db.getWorkTree(), "wt");
+
+ File worktreesDir = new File(db.getDirectory(), "worktrees");
+ File masterWorktreesDir = new File(worktreesDir, "wt");
+
+ FileRepository repository = new FileRepository(masterWorktreesDir);
+ try (Git git = new Git(repository)) {
+ ObjectId objectId = repository.resolve(HEAD);
+ assertNotNull(objectId);
+
+ Iterator<RevCommit> log = git.log().all().call().iterator();
+ assertTrue(log.hasNext());
+ assertTrue("Initial commit".equals(log.next().getShortMessage()));
+
+ // we have reflog entry
+ Collection<ReflogEntry> reflog = git.reflog().call();
+ assertNotNull(reflog);
+ assertTrue(reflog.size() > 0);
+ ReflogEntry[] reflogs = reflog.toArray(new ReflogEntry[0]);
+ assertEquals(reflogs[reflogs.length - 1].getComment(),
+ "reset: moving to HEAD");
+
+ // index works with file changes
+ File directory = trash.getParentFile();
+ File wtDir = new File(directory, "wt");
+ File testFile = new File(wtDir, "test");
+
+ Status status = git.status().call();
+ assertTrue(status.getUncommittedChanges().size() == 0);
+ assertTrue(status.getUntracked().size() == 0);
+
+ JGitTestUtil.write(testFile, "test");
+ status = git.status().call();
+ assertTrue(status.getUncommittedChanges().size() == 0);
+ assertTrue(status.getUntracked().size() == 1);
+
+ git.add().addFilepattern("test").call();
+ status = git.status().call();
+ assertTrue(status.getUncommittedChanges().size() == 1);
+ assertTrue(status.getUntracked().size() == 0);
+ }
+
+ }
+
+ private static void cloneBare(FS fs, File directory, String from, String to) throws IOException, InterruptedException {
+ ProcessBuilder builder = fs.runInShell("git",
+ new String[] { "clone", "--bare", from, to });
+ builder.directory(directory);
+ builder.environment().put("HOME", fs.userHome().getAbsolutePath());
+ StringBuilder input = new StringBuilder();
+ ExecutionResult result = fs.execute(builder, new ByteArrayInputStream(
+ input.toString().getBytes(StandardCharsets.UTF_8)));
+ String stdOut = toString(result.getStdout());
+ String errorOut = toString(result.getStderr());
+ assertNotNull(stdOut);
+ assertNotNull(errorOut);
+ }
+
+ private static void worktreeAddExisting(FS fs, File directory, String name) throws IOException, InterruptedException {
+ ProcessBuilder builder = fs.runInShell("git",
+ new String[] { "worktree", "add", "../" + name, name });
+ builder.directory(directory);
+ builder.environment().put("HOME", fs.userHome().getAbsolutePath());
+ StringBuilder input = new StringBuilder();
+ ExecutionResult result = fs.execute(builder, new ByteArrayInputStream(
+ input.toString().getBytes(StandardCharsets.UTF_8)));
+ String stdOut = toString(result.getStdout());
+ String errorOut = toString(result.getStderr());
+ assertNotNull(stdOut);
+ assertNotNull(errorOut);
+ }
+
+ private static void worktreeAddNew(FS fs, File directory, String name) throws IOException, InterruptedException {
+ ProcessBuilder builder = fs.runInShell("git",
+ new String[] { "worktree", "add", "-b", name, "../" + name, "master"});
+ builder.directory(directory);
+ builder.environment().put("HOME", fs.userHome().getAbsolutePath());
+ StringBuilder input = new StringBuilder();
+ ExecutionResult result = fs.execute(builder, new ByteArrayInputStream(
+ input.toString().getBytes(StandardCharsets.UTF_8)));
+ String stdOut = toString(result.getStdout());
+ String errorOut = toString(result.getStderr());
+ assertNotNull(stdOut);
+ assertNotNull(errorOut);
+ }
+
+ private static String toString(TemporaryBuffer b) throws IOException {
+ return RawParseUtils.decode(b.toByteArray());
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
index 917b6c3297..1ec506798c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
@@ -21,6 +21,9 @@ import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import java.io.File;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Iterator;
import java.util.regex.Pattern;
@@ -33,6 +36,7 @@ import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.lib.Sets;
@@ -45,6 +49,7 @@ import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.GitDateFormatter;
import org.eclipse.jgit.util.GitDateFormatter.Format;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.theories.DataPoints;
@@ -76,12 +81,12 @@ public class MergeCommandTest extends RepositoryTestCase {
assertEquals(MergeResult.MergeStatus.ALREADY_UP_TO_DATE, result.getMergeStatus());
}
// no reflog entry written by merge
- assertEquals("commit (initial): initial commit",
- db
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals("commit (initial): initial commit", refDb
.getReflogReader(Constants.HEAD).getLastEntry().getComment());
- assertEquals("commit (initial): initial commit",
- db
- .getReflogReader(db.getBranch()).getLastEntry().getComment());
+ assertEquals("commit (initial): initial commit", refDb
+ .getReflogReader(db.getFullBranch()).getLastEntry()
+ .getComment());
}
@Test
@@ -96,10 +101,11 @@ public class MergeCommandTest extends RepositoryTestCase {
assertEquals(second, result.getNewHead());
}
// no reflog entry written by merge
- assertEquals("commit: second commit", db
+ assertEquals("commit: second commit", db.getRefDatabase()
.getReflogReader(Constants.HEAD).getLastEntry().getComment());
- assertEquals("commit: second commit", db
- .getReflogReader(db.getBranch()).getLastEntry().getComment());
+ assertEquals("commit: second commit", db.getRefDatabase()
+ .getReflogReader(db.getFullBranch()).getLastEntry()
+ .getComment());
}
@Test
@@ -117,10 +123,13 @@ public class MergeCommandTest extends RepositoryTestCase {
assertEquals(MergeResult.MergeStatus.FAST_FORWARD, result.getMergeStatus());
assertEquals(second, result.getNewHead());
}
+ RefDatabase refDb = db.getRefDatabase();
assertEquals("merge refs/heads/master: Fast-forward",
- db.getReflogReader(Constants.HEAD).getLastEntry().getComment());
+ refDb.getReflogReader(Constants.HEAD)
+ .getLastEntry().getComment());
assertEquals("merge refs/heads/master: Fast-forward",
- db.getReflogReader(db.getBranch()).getLastEntry().getComment());
+ refDb.getReflogReader(db.getFullBranch())
+ .getLastEntry().getComment());
}
@Test
@@ -140,10 +149,12 @@ public class MergeCommandTest extends RepositoryTestCase {
result.getMergeStatus());
assertEquals(second, result.getNewHead());
}
- assertEquals("merge refs/heads/master: Fast-forward", db
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals("merge refs/heads/master: Fast-forward", refDb
.getReflogReader(Constants.HEAD).getLastEntry().getComment());
- assertEquals("merge refs/heads/master: Fast-forward", db
- .getReflogReader(db.getBranch()).getLastEntry().getComment());
+ assertEquals("merge refs/heads/master: Fast-forward", refDb
+ .getReflogReader(db.getFullBranch()).getLastEntry()
+ .getComment());
}
@Test
@@ -171,10 +182,12 @@ public class MergeCommandTest extends RepositoryTestCase {
assertEquals(MergeResult.MergeStatus.FAST_FORWARD, result.getMergeStatus());
assertEquals(second, result.getNewHead());
}
- assertEquals("merge refs/heads/master: Fast-forward",
- db.getReflogReader(Constants.HEAD).getLastEntry().getComment());
- assertEquals("merge refs/heads/master: Fast-forward",
- db.getReflogReader(db.getBranch()).getLastEntry().getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals("merge refs/heads/master: Fast-forward", refDb
+ .getReflogReader(Constants.HEAD).getLastEntry().getComment());
+ assertEquals("merge refs/heads/master: Fast-forward", refDb
+ .getReflogReader(db.getFullBranch()).getLastEntry()
+ .getComment());
}
@Test
@@ -229,14 +242,17 @@ public class MergeCommandTest extends RepositoryTestCase {
.include(db.exactRef(R_HEADS + MASTER)).call();
assertEquals(MergeStatus.MERGED, result.getMergeStatus());
}
+ RefDatabase refDb = db.getRefDatabase();
assertEquals(
"merge refs/heads/master: Merge made by "
+ mergeStrategy.getName() + ".",
- db.getReflogReader(Constants.HEAD).getLastEntry().getComment());
+ refDb.getReflogReader(Constants.HEAD).getLastEntry()
+ .getComment());
assertEquals(
"merge refs/heads/master: Merge made by "
+ mergeStrategy.getName() + ".",
- db.getReflogReader(db.getBranch()).getLastEntry().getComment());
+ refDb.getReflogReader(db.getFullBranch()).getLastEntry()
+ .getComment());
}
@Theory
@@ -662,14 +678,17 @@ public class MergeCommandTest extends RepositoryTestCase {
.setStrategy(MergeStrategy.RESOLVE).call();
assertEquals(MergeStatus.MERGED, result.getMergeStatus());
assertEquals("1\nb(1)\n3\n", read(new File(db.getWorkTree(), "b")));
- assertEquals("merge " + secondCommit.getId().getName()
- + ": Merge made by resolve.", db
- .getReflogReader(Constants.HEAD)
- .getLastEntry().getComment());
- assertEquals("merge " + secondCommit.getId().getName()
- + ": Merge made by resolve.", db
- .getReflogReader(db.getBranch())
- .getLastEntry().getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(
+ "merge " + secondCommit.getId().getName()
+ + ": Merge made by resolve.",
+ refDb.getReflogReader(Constants.HEAD).getLastEntry()
+ .getComment());
+ assertEquals(
+ "merge " + secondCommit.getId().getName()
+ + ": Merge made by resolve.",
+ refDb.getReflogReader(db.getFullBranch()).getLastEntry()
+ .getComment());
}
}
@@ -2086,6 +2105,94 @@ public class MergeCommandTest extends RepositoryTestCase {
}
}
+ @Test
+ public void testMergeCaseInsensitiveRename() throws Exception {
+ Assume.assumeTrue(
+ "Test makes only sense on a case-insensitive file system",
+ db.isWorkTreeCaseInsensitive());
+ try (Git git = new Git(db)) {
+ writeTrashFile("a", "aaa");
+ git.add().addFilepattern("a").call();
+ RevCommit initialCommit = git.commit().setMessage("initial").call();
+ // "Rename" "a" to "A"
+ git.rm().addFilepattern("a").call();
+ writeTrashFile("A", "aaa");
+ git.add().addFilepattern("A").call();
+ RevCommit master = git.commit().setMessage("rename to A").call();
+
+ createBranch(initialCommit, "refs/heads/side");
+ checkoutBranch("refs/heads/side");
+
+ writeTrashFile("b", "bbb");
+ git.add().addFilepattern("b").call();
+ git.commit().setMessage("side").call();
+
+ // Merge master into side
+ MergeResult result = git.merge().include(master)
+ .setStrategy(MergeStrategy.RECURSIVE).call();
+ assertEquals(MergeStatus.MERGED, result.getMergeStatus());
+ assertTrue(new File(db.getWorkTree(), "A").isFile());
+ // Double check
+ boolean found = true;
+ try (DirectoryStream<Path> dir = Files
+ .newDirectoryStream(db.getWorkTree().toPath())) {
+ for (Path p : dir) {
+ found = "A".equals(p.getFileName().toString());
+ if (found) {
+ break;
+ }
+ }
+ }
+ assertTrue(found);
+ }
+ }
+
+ @Test
+ public void testMergeCaseInsensitiveRenameConflict() throws Exception {
+ Assume.assumeTrue(
+ "Test makes only sense on a case-insensitive file system",
+ db.isWorkTreeCaseInsensitive());
+ try (Git git = new Git(db)) {
+ writeTrashFile("a", "aaa");
+ git.add().addFilepattern("a").call();
+ RevCommit initialCommit = git.commit().setMessage("initial").call();
+ // "Rename" "a" to "A" and change it
+ git.rm().addFilepattern("a").call();
+ writeTrashFile("A", "yyy");
+ git.add().addFilepattern("A").call();
+ RevCommit master = git.commit().setMessage("rename to A").call();
+
+ createBranch(initialCommit, "refs/heads/side");
+ checkoutBranch("refs/heads/side");
+
+ writeTrashFile("a", "xxx");
+ git.add().addFilepattern("a").call();
+ git.commit().setMessage("side").call();
+
+ // Merge master into side
+ MergeResult result = git.merge().include(master)
+ .setStrategy(MergeStrategy.RECURSIVE).call();
+ assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+ File a = new File(db.getWorkTree(), "A");
+ assertTrue(a.isFile());
+ // Double check
+ boolean found = true;
+ try (DirectoryStream<Path> dir = Files
+ .newDirectoryStream(db.getWorkTree().toPath())) {
+ for (Path p : dir) {
+ found = "A".equals(p.getFileName().toString());
+ if (found) {
+ break;
+ }
+ }
+ }
+ assertTrue(found);
+ assertEquals(1, result.getConflicts().size());
+ assertTrue(result.getConflicts().containsKey("a"));
+ checkFile(a, "yyy");
+ }
+ }
+
private static void setExecutable(Git git, String path, boolean executable) {
FS.DETECTED.setExecute(
new File(git.getRepository().getWorkTree(), path), executable);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
index 12300b3390..6d5e45c98f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
@@ -21,6 +21,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Map;
import java.util.concurrent.Callable;
import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
@@ -29,6 +30,7 @@ import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.IndexDiff.StageState;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
@@ -117,6 +119,7 @@ public class PullCommandTest extends RepositoryTestCase {
+ db.getWorkTree().getAbsolutePath();
assertEquals(message, mergeCommit.getShortMessage());
}
+ assertTrue(target.status().call().isClean());
}
@Test
@@ -153,6 +156,10 @@ public class PullCommandTest extends RepositoryTestCase {
assertFileContentsEqual(targetFile, result);
assertEquals(RepositoryState.MERGING, target.getRepository()
.getRepositoryState());
+ Status status = target.status().call();
+ Map<String, StageState> conflicting = status.getConflictingStageState();
+ assertEquals(1, conflicting.size());
+ assertEquals(StageState.BOTH_MODIFIED, conflicting.get("SomeFile.txt"));
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
index 70e990dedf..d1696d62a8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
@@ -22,6 +22,7 @@ import java.io.IOException;
import java.io.PrintStream;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
+import java.time.Instant;
import java.util.Properties;
import org.eclipse.jgit.api.errors.DetachedHeadException;
@@ -1146,7 +1147,7 @@ public class PushCommandTest extends RepositoryTestCase {
RevCommit commit2 = git2.commit().setMessage("adding a").call();
// run a gc to ensure we have a bitmap index
- Properties res = git1.gc().setExpire(null).call();
+ Properties res = git1.gc().setExpire((Instant) null).call();
assertEquals(8, res.size());
// create another commit so we have something else to push
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
index 02e3a2e06f..4c8cf06a67 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
@@ -24,6 +24,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -55,6 +57,7 @@ import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RebaseTodoLine;
import org.eclipse.jgit.lib.RebaseTodoLine.Action;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.RepositoryState;
@@ -131,11 +134,12 @@ public class RebaseCommandTest extends RepositoryTestCase {
checkFile(file2, "file2");
assertEquals(Status.FAST_FORWARD, res.getStatus());
- List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
+ RefDatabase refDb = db.getRefDatabase();
+ List<ReflogEntry> headLog = refDb.getReflogReader(Constants.HEAD)
.getReverseEntries();
- List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
+ List<ReflogEntry> topicLog = refDb.getReflogReader("refs/heads/topic")
.getReverseEntries();
- List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
+ List<ReflogEntry> masterLog = refDb.getReflogReader("refs/heads/master")
.getReverseEntries();
assertEquals("rebase finished: returning to refs/heads/topic", headLog
.get(0).getComment());
@@ -177,11 +181,12 @@ public class RebaseCommandTest extends RepositoryTestCase {
checkFile(file2, "file2 new content");
assertEquals(Status.FAST_FORWARD, res.getStatus());
- List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
+ RefDatabase refDb = db.getRefDatabase();
+ List<ReflogEntry> headLog = refDb.getReflogReader(Constants.HEAD)
.getReverseEntries();
- List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
+ List<ReflogEntry> topicLog = refDb.getReflogReader("refs/heads/topic")
.getReverseEntries();
- List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
+ List<ReflogEntry> masterLog = refDb.getReflogReader("refs/heads/master")
.getReverseEntries();
assertEquals("rebase finished: returning to refs/heads/topic", headLog
.get(0).getComment());
@@ -445,13 +450,14 @@ public class RebaseCommandTest extends RepositoryTestCase {
assertEquals(a, rw.next());
}
- List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
+ RefDatabase refDb = db.getRefDatabase();
+ List<ReflogEntry> headLog = refDb.getReflogReader(Constants.HEAD)
.getReverseEntries();
- List<ReflogEntry> sideLog = db.getReflogReader("refs/heads/side")
+ List<ReflogEntry> sideLog = refDb.getReflogReader("refs/heads/side")
.getReverseEntries();
- List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
+ List<ReflogEntry> topicLog = refDb.getReflogReader("refs/heads/topic")
.getReverseEntries();
- List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
+ List<ReflogEntry> masterLog = refDb.getReflogReader("refs/heads/master")
.getReverseEntries();
assertEquals("rebase finished: returning to refs/heads/topic", headLog
.get(0).getComment());
@@ -766,9 +772,10 @@ public class RebaseCommandTest extends RepositoryTestCase {
RebaseResult result = git.rebase().setUpstream(parent).call();
assertEquals(Status.UP_TO_DATE, result.getStatus());
- assertEquals(2, db.getReflogReader(Constants.HEAD).getReverseEntries()
- .size());
- assertEquals(2, db.getReflogReader("refs/heads/master")
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(2, refDb.getReflogReader(Constants.HEAD)
+ .getReverseEntries().size());
+ assertEquals(2, refDb.getReflogReader("refs/heads/master")
.getReverseEntries().size());
}
@@ -784,9 +791,10 @@ public class RebaseCommandTest extends RepositoryTestCase {
RebaseResult res = git.rebase().setUpstream(first).call();
assertEquals(Status.UP_TO_DATE, res.getStatus());
- assertEquals(1, db.getReflogReader(Constants.HEAD).getReverseEntries()
- .size());
- assertEquals(1, db.getReflogReader("refs/heads/master")
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(1, refDb.getReflogReader(Constants.HEAD)
+ .getReverseEntries().size());
+ assertEquals(1, refDb.getReflogReader("refs/heads/master")
.getReverseEntries().size());
}
@@ -844,11 +852,12 @@ public class RebaseCommandTest extends RepositoryTestCase {
db.resolve(Constants.HEAD)).getParent(0));
}
assertEquals(origHead, db.readOrigHead());
- List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
+ RefDatabase refDb = db.getRefDatabase();
+ List<ReflogEntry> headLog = refDb.getReflogReader(Constants.HEAD)
.getReverseEntries();
- List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
+ List<ReflogEntry> topicLog = refDb.getReflogReader("refs/heads/topic")
.getReverseEntries();
- List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
+ List<ReflogEntry> masterLog = refDb.getReflogReader("refs/heads/master")
.getReverseEntries();
assertEquals(2, masterLog.size());
assertEquals(3, topicLog.size());
@@ -896,8 +905,8 @@ public class RebaseCommandTest extends RepositoryTestCase {
db.resolve(Constants.HEAD)).getParent(0));
}
- List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
- .getReverseEntries();
+ List<ReflogEntry> headLog = db.getRefDatabase()
+ .getReflogReader(Constants.HEAD).getReverseEntries();
assertEquals(8, headLog.size());
assertEquals("rebase: change file1 in topic", headLog.get(0)
.getComment());
@@ -1603,7 +1612,7 @@ public class RebaseCommandTest extends RepositoryTestCase {
public void testAuthorScriptConverter() throws Exception {
// -1 h timezone offset
PersonIdent ident = new PersonIdent("Author name", "a.mail@some.com",
- 123456789123L, -60);
+ Instant.ofEpochMilli(123456789123L), ZoneOffset.ofHours(-1));
String convertedAuthor = git.rebase().toAuthorScript(ident);
String[] lines = convertedAuthor.split("\n");
assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]);
@@ -1615,12 +1624,14 @@ public class RebaseCommandTest extends RepositoryTestCase {
assertEquals(ident.getName(), parsedIdent.getName());
assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress());
// this is rounded to the last second
- assertEquals(123456789000L, parsedIdent.getWhen().getTime());
- assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset());
+ assertEquals(123456789000L,
+ parsedIdent.getWhenAsInstant().toEpochMilli());
+ assertEquals(ident.getZoneId(), parsedIdent.getZoneId());
// + 9.5h timezone offset
ident = new PersonIdent("Author name", "a.mail@some.com",
- 123456789123L, +570);
+ Instant.ofEpochMilli(123456789123L),
+ ZoneOffset.ofHoursMinutes(9, 30));
convertedAuthor = git.rebase().toAuthorScript(ident);
lines = convertedAuthor.split("\n");
assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]);
@@ -1631,8 +1642,9 @@ public class RebaseCommandTest extends RepositoryTestCase {
convertedAuthor.getBytes(UTF_8));
assertEquals(ident.getName(), parsedIdent.getName());
assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress());
- assertEquals(123456789000L, parsedIdent.getWhen().getTime());
- assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset());
+ assertEquals(123456789000L,
+ parsedIdent.getWhenAsInstant().toEpochMilli());
+ assertEquals(ident.getZoneId(), parsedIdent.getZoneId());
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java
index 534ebd9c61..add5886c2d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java
@@ -118,23 +118,21 @@ public class RenameBranchCommandTest extends RepositoryTestCase {
String branch = "b1";
assertEquals(BranchRebaseMode.REBASE,
- config.getEnum(BranchRebaseMode.values(),
- ConfigConstants.CONFIG_BRANCH_SECTION, Constants.MASTER,
- ConfigConstants.CONFIG_KEY_REBASE,
+ config.getEnum(ConfigConstants.CONFIG_BRANCH_SECTION,
+ Constants.MASTER, ConfigConstants.CONFIG_KEY_REBASE,
BranchRebaseMode.NONE));
assertNull(config.getEnum(BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, branch,
- ConfigConstants.CONFIG_KEY_REBASE, null));
+ ConfigConstants.CONFIG_KEY_REBASE));
assertNotNull(git.branchRename().setNewName(branch).call());
config = git.getRepository().getConfig();
assertNull(config.getEnum(BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, Constants.MASTER,
- ConfigConstants.CONFIG_KEY_REBASE, null));
+ ConfigConstants.CONFIG_KEY_REBASE));
assertEquals(BranchRebaseMode.REBASE,
- config.getEnum(BranchRebaseMode.values(),
- ConfigConstants.CONFIG_BRANCH_SECTION, branch,
+ config.getEnum(ConfigConstants.CONFIG_BRANCH_SECTION, branch,
ConfigConstants.CONFIG_KEY_REBASE,
BranchRebaseMode.NONE));
}
@@ -170,13 +168,12 @@ public class RenameBranchCommandTest extends RepositoryTestCase {
String branch = "b1";
assertEquals(BranchRebaseMode.REBASE,
- config.getEnum(BranchRebaseMode.values(),
- ConfigConstants.CONFIG_BRANCH_SECTION, Constants.MASTER,
- ConfigConstants.CONFIG_KEY_REBASE,
+ config.getEnum(ConfigConstants.CONFIG_BRANCH_SECTION,
+ Constants.MASTER, ConfigConstants.CONFIG_KEY_REBASE,
BranchRebaseMode.NONE));
assertNull(config.getEnum(BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, branch,
- ConfigConstants.CONFIG_KEY_REBASE, null));
+ ConfigConstants.CONFIG_KEY_REBASE));
assertTrue(config.getBoolean(ConfigConstants.CONFIG_BRANCH_SECTION,
Constants.MASTER, ConfigConstants.CONFIG_KEY_MERGE, true));
assertFalse(config.getBoolean(ConfigConstants.CONFIG_BRANCH_SECTION,
@@ -187,10 +184,9 @@ public class RenameBranchCommandTest extends RepositoryTestCase {
config = git.getRepository().getConfig();
assertNull(config.getEnum(BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, Constants.MASTER,
- ConfigConstants.CONFIG_KEY_REBASE, null));
+ ConfigConstants.CONFIG_KEY_REBASE));
assertEquals(BranchRebaseMode.REBASE,
- config.getEnum(BranchRebaseMode.values(),
- ConfigConstants.CONFIG_BRANCH_SECTION, branch,
+ config.getEnum(ConfigConstants.CONFIG_BRANCH_SECTION, branch,
ConfigConstants.CONFIG_KEY_REBASE,
BranchRebaseMode.NONE));
assertFalse(config.getBoolean(ConfigConstants.CONFIG_BRANCH_SECTION,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
index 8a479a0ca0..99873e1be1 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
@@ -36,11 +36,13 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FileUtils;
import org.junit.Assert;
+import org.junit.Assume;
import org.junit.Test;
public class ResetCommandTest extends RepositoryTestCase {
@@ -554,46 +556,73 @@ public class ResetCommandTest extends RepositoryTestCase {
assertNull(db.resolve(Constants.HEAD));
}
+ @Test
+ public void testHardResetFileMode() throws Exception {
+ Assume.assumeTrue("Test must be able to set executable bit",
+ db.getFS().supportsExecute());
+ git = new Git(db);
+ File a = writeTrashFile("a.txt", "aaa");
+ File b = writeTrashFile("b.txt", "bbb");
+ db.getFS().setExecute(b, true);
+ assertFalse(db.getFS().canExecute(a));
+ assertTrue(db.getFS().canExecute(b));
+ git.add().addFilepattern("a.txt").addFilepattern("b.txt").call();
+ RevCommit commit = git.commit().setMessage("files created").call();
+ db.getFS().setExecute(a, true);
+ db.getFS().setExecute(b, false);
+ assertTrue(db.getFS().canExecute(a));
+ assertFalse(db.getFS().canExecute(b));
+ git.add().addFilepattern("a.txt").addFilepattern("b.txt").call();
+ git.commit().setMessage("change exe bits").call();
+ Ref ref = git.reset().setRef(commit.getName()).setMode(HARD).call();
+ assertSameAsHead(ref);
+ assertEquals(commit.getId(), ref.getObjectId());
+ assertFalse(db.getFS().canExecute(a));
+ assertTrue(db.getFS().canExecute(b));
+ }
+
private void assertReflog(ObjectId prevHead, ObjectId head)
throws IOException {
// Check the reflog for HEAD
- String actualHeadMessage = db.getReflogReader(Constants.HEAD)
+ RefDatabase refDb = db.getRefDatabase();
+ String actualHeadMessage = refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getComment();
String expectedHeadMessage = head.getName() + ": updating HEAD";
assertEquals(expectedHeadMessage, actualHeadMessage);
- assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
+ assertEquals(head.getName(), refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getNewId().getName());
- assertEquals(prevHead.getName(), db.getReflogReader(Constants.HEAD)
+ assertEquals(prevHead.getName(), refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getOldId().getName());
// The reflog for master contains the same as the one for HEAD
- String actualMasterMessage = db.getReflogReader("refs/heads/master")
+ String actualMasterMessage = refDb.getReflogReader("refs/heads/master")
.getLastEntry().getComment();
String expectedMasterMessage = head.getName() + ": updating HEAD"; // yes!
assertEquals(expectedMasterMessage, actualMasterMessage);
- assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
+ assertEquals(head.getName(), refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getNewId().getName());
- assertEquals(prevHead.getName(), db
- .getReflogReader("refs/heads/master").getLastEntry().getOldId()
- .getName());
+ assertEquals(prevHead.getName(),
+ refDb.getReflogReader("refs/heads/master").getLastEntry()
+ .getOldId().getName());
}
private void assertReflogDisabled(ObjectId head)
throws IOException {
+ RefDatabase refDb = db.getRefDatabase();
// Check the reflog for HEAD
- String actualHeadMessage = db.getReflogReader(Constants.HEAD)
+ String actualHeadMessage = refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getComment();
String expectedHeadMessage = "commit: adding a.txt and dir/b.txt";
assertEquals(expectedHeadMessage, actualHeadMessage);
- assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
+ assertEquals(head.getName(), refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getOldId().getName());
// The reflog for master contains the same as the one for HEAD
- String actualMasterMessage = db.getReflogReader("refs/heads/master")
+ String actualMasterMessage = refDb.getReflogReader("refs/heads/master")
.getLastEntry().getComment();
String expectedMasterMessage = "commit: adding a.txt and dir/b.txt";
assertEquals(expectedMasterMessage, actualMasterMessage);
- assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
+ assertEquals(head.getName(), refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getOldId().getName());
}
/**
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java
index 4ebe994ef7..89fdb32220 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Robin Rosenberg and others
+ * Copyright (C) 2011, 2024 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
@@ -29,6 +29,7 @@ import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
@@ -59,7 +60,9 @@ public class RevertCommandTest extends RepositoryTestCase {
writeTrashFile("a",
"first line\nsecond line\nthird line\nfourth line\n");
git.add().addFilepattern("a").call();
- RevCommit fixingA = git.commit().setMessage("fixed a").call();
+ // Commit message with a non-empty second line on purpose
+ RevCommit fixingA = git.commit().setMessage("fixed a\nsecond line")
+ .call();
writeTrashFile("b", "first line\n");
git.add().addFilepattern("b").call();
@@ -78,16 +81,18 @@ public class RevertCommandTest extends RepositoryTestCase {
+ "This reverts commit " + fixingA.getId().getName() + ".\n";
assertEquals(expectedMessage, revertCommit.getFullMessage());
assertEquals("fixed b", history.next().getFullMessage());
- assertEquals("fixed a", history.next().getFullMessage());
+ assertEquals("fixed a\nsecond line",
+ history.next().getFullMessage());
assertEquals("enlarged a", history.next().getFullMessage());
assertEquals("create b", history.next().getFullMessage());
assertEquals("create a", history.next().getFullMessage());
assertFalse(history.hasNext());
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
assertTrue(reader.getLastEntry().getComment()
.startsWith("revert: Revert \""));
- reader = db.getReflogReader(db.getBranch());
+ reader = refDb.getReflogReader(db.getFullBranch());
assertTrue(reader.getLastEntry().getComment()
.startsWith("revert: Revert \""));
}
@@ -167,10 +172,11 @@ public class RevertCommandTest extends RepositoryTestCase {
assertEquals("add first", history.next().getFullMessage());
assertFalse(history.hasNext());
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
assertTrue(reader.getLastEntry().getComment()
.startsWith("revert: Revert \""));
- reader = db.getReflogReader(db.getBranch());
+ reader = refDb.getReflogReader(db.getFullBranch());
assertTrue(reader.getLastEntry().getComment()
.startsWith("revert: Revert \""));
}
@@ -220,10 +226,11 @@ public class RevertCommandTest extends RepositoryTestCase {
assertEquals("add first", history.next().getFullMessage());
assertFalse(history.hasNext());
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
assertTrue(reader.getLastEntry().getComment()
.startsWith("revert: Revert \""));
- reader = db.getReflogReader(db.getBranch());
+ reader = refDb.getReflogReader(db.getFullBranch());
assertTrue(reader.getLastEntry().getComment()
.startsWith("revert: Revert \""));
}
@@ -428,12 +435,13 @@ public class RevertCommandTest extends RepositoryTestCase {
assertEquals(RepositoryState.SAFE, db.getRepositoryState());
if (reason == null) {
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
- assertTrue(reader.getLastEntry().getComment()
- .startsWith("revert: "));
- reader = db.getReflogReader(db.getBranch());
- assertTrue(reader.getLastEntry().getComment()
- .startsWith("revert: "));
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
+ assertTrue(
+ reader.getLastEntry().getComment().startsWith("revert: "));
+ reader = refDb.getReflogReader(db.getFullBranch());
+ assertTrue(
+ reader.getLastEntry().getComment().startsWith("revert: "));
}
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerMissingPermissionsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerMissingPermissionsTest.java
deleted file mode 100644
index d0fbdbd090..0000000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerMissingPermissionsTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (c) 2019 Alex Jitianu <alex_jitianu@sync.ro> 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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.Policy;
-import java.util.Collections;
-
-import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.util.FileUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Tests that using a SecurityManager does not result in errors logged.
- */
-public class SecurityManagerMissingPermissionsTest extends RepositoryTestCase {
-
- /**
- * Collects all logging sent to the logging system.
- */
- private final ByteArrayOutputStream errorOutput = new ByteArrayOutputStream();
-
- private SecurityManager originalSecurityManager;
-
- private PrintStream defaultErrorOutput;
-
- @Override
- @Before
- public void setUp() throws Exception {
- originalSecurityManager = System.getSecurityManager();
-
- // slf4j-simple logs to System.err, redirect it to enable asserting
- // logged errors
- defaultErrorOutput = System.err;
- System.setErr(new PrintStream(errorOutput));
-
- refreshPolicyAllPermission(Policy.getPolicy());
- System.setSecurityManager(new SecurityManager());
- super.setUp();
- }
-
- /**
- * If a SecurityManager is active a lot of {@link java.io.FilePermission}
- * errors are thrown and logged while initializing a repository.
- *
- * @throws Exception
- */
- @Test
- public void testCreateNewRepos_MissingPermissions() throws Exception {
- File wcTree = new File(getTemporaryDirectory(),
- "CreateNewRepositoryTest_testCreateNewRepos");
-
- File marker = new File(getTemporaryDirectory(), "marker");
- Files.write(marker.toPath(), Collections.singletonList("Can write"));
- assertTrue("Can write in test directory", marker.isFile());
- FileUtils.delete(marker);
- assertFalse("Can delete in test direcory", marker.exists());
-
- Git git = Git.init().setBare(false)
- .setDirectory(new File(wcTree.getAbsolutePath())).call();
-
- addRepoToClose(git.getRepository());
-
- assertEquals("", errorOutput.toString());
- }
-
- @Override
- @After
- public void tearDown() throws Exception {
- System.setSecurityManager(originalSecurityManager);
- System.setErr(defaultErrorOutput);
- super.tearDown();
- }
-
- /**
- * Refresh the Java Security Policy.
- *
- * @param policy
- * the policy object
- *
- * @throws IOException
- * if the temporary file that contains the policy could not be
- * created
- */
- private static void refreshPolicyAllPermission(Policy policy)
- throws IOException {
- // Starting with an all permissions policy.
- String policyString = "grant { permission java.security.AllPermission; };";
-
- // Do not use TemporaryFilesFactory, it will create a dependency cycle
- Path policyFile = Files.createTempFile("testpolicy", ".txt");
-
- try {
- Files.write(policyFile, Collections.singletonList(policyString));
- System.setProperty("java.security.policy",
- policyFile.toUri().toURL().toString());
- policy.refresh();
- } finally {
- try {
- Files.delete(policyFile);
- } catch (IOException e) {
- // Do not log; the test tests for no logging having occurred
- e.printStackTrace();
- }
- }
- }
-
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerTest.java
deleted file mode 100644
index 2b930a1133..0000000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerTest.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2019 Nail Samatov <sanail@yandex.ru> 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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.io.FilePermission;
-import java.io.IOException;
-import java.lang.reflect.ReflectPermission;
-import java.nio.file.Files;
-import java.security.Permission;
-import java.security.SecurityPermission;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.PropertyPermission;
-import java.util.logging.LoggingPermission;
-
-import javax.security.auth.AuthPermission;
-
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.junit.JGitTestUtil;
-import org.eclipse.jgit.junit.MockSystemReader;
-import org.eclipse.jgit.junit.SeparateClassloaderTestRunner;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.util.FileUtils;
-import org.eclipse.jgit.util.SystemReader;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * <p>
- * Tests if jgit works if SecurityManager is enabled.
- * </p>
- *
- * <p>
- * Note: JGit's classes shouldn't be used before SecurityManager is configured.
- * If you use some JGit's class before SecurityManager is replaced then part of
- * the code can be invoked outside of our custom SecurityManager and this test
- * becomes useless.
- * </p>
- *
- * <p>
- * For example the class {@link org.eclipse.jgit.util.FS} is used widely in jgit
- * sources. It contains DETECTED static field. At the first usage of the class
- * FS the field DETECTED is initialized and during initialization many system
- * operations that SecurityManager can forbid are invoked.
- * </p>
- *
- * <p>
- * For this reason this test doesn't extend LocalDiskRepositoryTestCase (it uses
- * JGit's classes in setUp() method) and other JGit's utility classes. It's done
- * to affect SecurityManager as less as possible.
- * </p>
- *
- * <p>
- * We use SeparateClassloaderTestRunner to isolate FS.DETECTED field
- * initialization between different tests run.
- * </p>
- */
-@RunWith(SeparateClassloaderTestRunner.class)
-public class SecurityManagerTest {
- private File root;
-
- private SecurityManager originalSecurityManager;
-
- private List<Permission> permissions = new ArrayList<>();
-
- @Before
- public void setUp() throws Exception {
- // Create working directory
- SystemReader.setInstance(new MockSystemReader());
- root = Files.createTempDirectory("jgit-security").toFile();
-
- // Add system permissions
- permissions.add(new RuntimePermission("*"));
- permissions.add(new SecurityPermission("*"));
- permissions.add(new AuthPermission("*"));
- permissions.add(new ReflectPermission("*"));
- permissions.add(new PropertyPermission("*", "read,write"));
- permissions.add(new LoggingPermission("control", null));
-
- permissions.add(new FilePermission(
- System.getProperty("java.home") + "/-", "read"));
-
- String tempDir = System.getProperty("java.io.tmpdir");
- permissions.add(new FilePermission(tempDir, "read,write,delete"));
- permissions
- .add(new FilePermission(tempDir + "/-", "read,write,delete"));
-
- // Add permissions to dependent jar files.
- String classPath = System.getProperty("java.class.path");
- if (classPath != null) {
- for (String path : classPath.split(File.pathSeparator)) {
- permissions.add(new FilePermission(path, "read"));
- }
- }
- // Add permissions to jgit class files.
- String jgitSourcesRoot = new File(System.getProperty("user.dir"))
- .getParent();
- permissions.add(new FilePermission(jgitSourcesRoot + "/-", "read"));
-
- // Add permissions to working dir for jgit. Our git repositories will be
- // initialized and cloned here.
- permissions.add(new FilePermission(root.getPath() + "/-",
- "read,write,delete,execute"));
-
- // Replace Security Manager
- originalSecurityManager = System.getSecurityManager();
- System.setSecurityManager(new SecurityManager() {
-
- @Override
- public void checkPermission(Permission requested) {
- for (Permission permission : permissions) {
- if (permission.implies(requested)) {
- return;
- }
- }
-
- super.checkPermission(requested);
- }
- });
- }
-
- @After
- public void tearDown() throws Exception {
- System.setSecurityManager(originalSecurityManager);
-
- // Note: don't use this method before security manager is replaced in
- // setUp() method. The method uses FS.DETECTED internally and can affect
- // the test.
- FileUtils.delete(root, FileUtils.RECURSIVE | FileUtils.RETRY);
- }
-
- @Test
- public void testInitAndClone() throws IOException, GitAPIException {
- File remote = new File(root, "remote");
- File local = new File(root, "local");
-
- try (Git git = Git.init().setDirectory(remote).call()) {
- JGitTestUtil.write(new File(remote, "hello.txt"), "Hello world!");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("Initial commit").call();
- }
-
- try (Git git = Git.cloneRepository().setURI(remote.toURI().toString())
- .setDirectory(local).call()) {
- assertTrue(new File(local, ".git").exists());
-
- JGitTestUtil.write(new File(local, "hi.txt"), "Hi!");
- git.add().addFilepattern(".").call();
- RevCommit commit1 = git.commit().setMessage("Commit on local repo")
- .call();
- assertEquals("Commit on local repo", commit1.getFullMessage());
- assertNotNull(TreeWalk.forPath(git.getRepository(), "hello.txt",
- commit1.getTree()));
- }
-
- }
-
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
index 5d0ab05174..18cd21a5d7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
@@ -409,8 +409,8 @@ public class StashCreateCommandTest extends RepositoryTestCase {
assertEquals("content", read(committedFile));
validateStashedCommit(stashed);
- ReflogReader reader = git.getRepository().getReflogReader(
- Constants.R_STASH);
+ ReflogReader reader = git.getRepository().getRefDatabase()
+ .getReflogReader(Constants.R_STASH);
ReflogEntry entry = reader.getLastEntry();
assertNotNull(entry);
assertEquals(ObjectId.zeroId(), entry.getOldId());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
index c81731d746..d937579283 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
@@ -92,8 +92,8 @@ public class StashDropCommandTest extends RepositoryTestCase {
stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
- ReflogReader reader = git.getRepository().getReflogReader(
- Constants.R_STASH);
+ ReflogReader reader = git.getRepository().getRefDatabase()
+ .getReflogReader(Constants.R_STASH);
assertNull(reader);
}
@@ -120,8 +120,8 @@ public class StashDropCommandTest extends RepositoryTestCase {
assertNull(git.stashDrop().setAll(true).call());
assertNull(git.getRepository().exactRef(Constants.R_STASH));
- ReflogReader reader = git.getRepository().getReflogReader(
- Constants.R_STASH);
+ ReflogReader reader = git.getRepository().getRefDatabase()
+ .getReflogReader(Constants.R_STASH);
assertNull(reader);
}
@@ -150,8 +150,8 @@ public class StashDropCommandTest extends RepositoryTestCase {
assertNotNull(stashRef);
assertEquals(firstStash, stashRef.getObjectId());
- ReflogReader reader = git.getRepository().getReflogReader(
- Constants.R_STASH);
+ ReflogReader reader = git.getRepository().getRefDatabase()
+ .getReflogReader(Constants.R_STASH);
List<ReflogEntry> entries = reader.getReverseEntries();
assertEquals(1, entries.size());
assertEquals(ObjectId.zeroId(), entries.get(0).getOldId());
@@ -192,8 +192,8 @@ public class StashDropCommandTest extends RepositoryTestCase {
assertNotNull(stashRef);
assertEquals(thirdStash, stashRef.getObjectId());
- ReflogReader reader = git.getRepository().getReflogReader(
- Constants.R_STASH);
+ ReflogReader reader = git.getRepository().getRefDatabase()
+ .getReflogReader(Constants.R_STASH);
List<ReflogEntry> entries = reader.getReverseEntries();
assertEquals(2, entries.size());
assertEquals(ObjectId.zeroId(), entries.get(1).getOldId());
@@ -250,8 +250,8 @@ public class StashDropCommandTest extends RepositoryTestCase {
assertNotNull(stashRef);
assertEquals(thirdStash, stashRef.getObjectId());
- ReflogReader reader = git.getRepository().getReflogReader(
- Constants.R_STASH);
+ ReflogReader reader = git.getRepository().getRefDatabase()
+ .getReflogReader(Constants.R_STASH);
List<ReflogEntry> entries = reader.getReverseEntries();
assertEquals(2, entries.size());
assertEquals(ObjectId.zeroId(), entries.get(1).getOldId());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java
index f47f447375..c2c06b2477 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java
@@ -23,20 +23,22 @@ import org.junit.Test;
/** Unit tests of {@link BlameGenerator}. */
public class BlameGeneratorTest extends RepositoryTestCase {
+ private static final String FILE = "file.txt";
+
@Test
public void testBoundLineDelete() throws Exception {
try (Git git = new Git(db)) {
String[] content1 = new String[] { "first", "second" };
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
+ writeTrashFile(FILE, join(content1));
+ git.add().addFilepattern(FILE).call();
RevCommit c1 = git.commit().setMessage("create file").call();
String[] content2 = new String[] { "third", "first", "second" };
- writeTrashFile("file.txt", join(content2));
- git.add().addFilepattern("file.txt").call();
+ writeTrashFile(FILE, join(content2));
+ git.add().addFilepattern(FILE).call();
RevCommit c2 = git.commit().setMessage("create file").call();
- try (BlameGenerator generator = new BlameGenerator(db, "file.txt")) {
+ try (BlameGenerator generator = new BlameGenerator(db, FILE)) {
generator.push(null, db.resolve(Constants.HEAD));
assertEquals(3, generator.getResultContents().size());
@@ -47,7 +49,7 @@ public class BlameGeneratorTest extends RepositoryTestCase {
assertEquals(1, generator.getResultEnd());
assertEquals(0, generator.getSourceStart());
assertEquals(1, generator.getSourceEnd());
- assertEquals("file.txt", generator.getSourcePath());
+ assertEquals(FILE, generator.getSourcePath());
assertTrue(generator.next());
assertEquals(c1, generator.getSourceCommit());
@@ -56,7 +58,7 @@ public class BlameGeneratorTest extends RepositoryTestCase {
assertEquals(3, generator.getResultEnd());
assertEquals(0, generator.getSourceStart());
assertEquals(2, generator.getSourceEnd());
- assertEquals("file.txt", generator.getSourcePath());
+ assertEquals(FILE, generator.getSourcePath());
assertFalse(generator.next());
}
@@ -87,7 +89,8 @@ public class BlameGeneratorTest extends RepositoryTestCase {
git.add().addFilepattern(FILENAME_2).call();
RevCommit c2 = git.commit().setMessage("change file2").call();
- try (BlameGenerator generator = new BlameGenerator(db, FILENAME_2)) {
+ try (BlameGenerator generator = new BlameGenerator(db,
+ FILENAME_2)) {
generator.push(null, db.resolve(Constants.HEAD));
assertEquals(3, generator.getResultContents().size());
@@ -113,7 +116,8 @@ public class BlameGeneratorTest extends RepositoryTestCase {
}
// and test again with other BlameGenerator API:
- try (BlameGenerator generator = new BlameGenerator(db, FILENAME_2)) {
+ try (BlameGenerator generator = new BlameGenerator(db,
+ FILENAME_2)) {
generator.push(null, db.resolve(Constants.HEAD));
BlameResult result = generator.computeBlameResult();
@@ -136,21 +140,21 @@ public class BlameGeneratorTest extends RepositoryTestCase {
try (Git git = new Git(db)) {
String[] content1 = new String[] { "first", "second", "third" };
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
+ writeTrashFile(FILE, join(content1));
+ git.add().addFilepattern(FILE).call();
git.commit().setMessage("create file").call();
String[] content2 = new String[] { "" };
- writeTrashFile("file.txt", join(content2));
- git.add().addFilepattern("file.txt").call();
+ writeTrashFile(FILE, join(content2));
+ git.add().addFilepattern(FILE).call();
git.commit().setMessage("create file").call();
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
+ writeTrashFile(FILE, join(content1));
+ git.add().addFilepattern(FILE).call();
RevCommit c3 = git.commit().setMessage("create file").call();
- try (BlameGenerator generator = new BlameGenerator(db, "file.txt")) {
+ try (BlameGenerator generator = new BlameGenerator(db, FILE)) {
generator.push(null, db.resolve(Constants.HEAD));
assertEquals(3, generator.getResultContents().size());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java
index 7fb98ec53b..c41dd81add 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java
@@ -584,7 +584,7 @@ public class AttributesHandlerTest extends RepositoryTestCase {
}
if (infoAttributesContent != null) {
- File f = new File(db.getDirectory(), Constants.INFO_ATTRIBUTES);
+ File f = new File(db.getCommonDirectory(), Constants.INFO_ATTRIBUTES);
write(f, infoAttributesContent);
}
config.save();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
index f23469eda0..35b953320e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
@@ -26,6 +26,7 @@ import org.eclipse.jgit.attributes.Attribute.State;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.junit.Before;
import org.junit.Test;
@@ -230,10 +231,10 @@ public class AttributesNodeDirCacheIteratorTest extends RepositoryTestCase {
else {
Attributes entryAttributes = new Attributes();
- new AttributesHandler(walk).mergeAttributes(attributesNode,
- pathName,
- false,
- entryAttributes);
+ new AttributesHandler(walk,
+ () -> walk.getTree(CanonicalTreeParser.class))
+ .mergeAttributes(attributesNode, pathName, false,
+ entryAttributes);
if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
for (Attribute attribute : nodeAttrs) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
index 1fcfbaf0fa..dbbcb75da9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
@@ -20,6 +20,7 @@ import java.io.InputStream;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.junit.After;
import org.junit.Test;
@@ -156,8 +157,9 @@ public class AttributesNodeTest {
private void assertAttribute(String path, AttributesNode node,
Attributes attrs) throws IOException {
Attributes attributes = new Attributes();
- new AttributesHandler(DUMMY_WALK).mergeAttributes(node, path, false,
- attributes);
+ new AttributesHandler(DUMMY_WALK,
+ () -> DUMMY_WALK.getTree(CanonicalTreeParser.class))
+ .mergeAttributes(node, path, false, attributes);
assertEquals(attrs, attributes);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
index 7b573e122e..c6c91386a2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
@@ -26,6 +26,7 @@ import org.eclipse.jgit.attributes.Attribute.State;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
@@ -194,9 +195,10 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase {
else {
Attributes entryAttributes = new Attributes();
- new AttributesHandler(walk).mergeAttributes(attributesNode,
- pathName, false,
- entryAttributes);
+ new AttributesHandler(walk,
+ () -> walk.getTree(CanonicalTreeParser.class))
+ .mergeAttributes(attributesNode, pathName, false,
+ entryAttributes);
if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
for (Attribute attribute : nodeAttrs) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java
index 009ca8a152..ac30c6c526 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java
@@ -268,6 +268,51 @@ public class MergeGitAttributeTest extends RepositoryTestCase {
}
@Test
+ public void mergeTextualFile_SetUnionMerge() throws NoWorkTreeException,
+ NoFilepatternException, GitAPIException, IOException {
+ try (Git git = createRepositoryBinaryConflict(g -> {
+ try {
+ writeTrashFile(".gitattributes", "*.cat merge=union");
+ writeTrashFile("main.cat", "A\n" + "B\n" + "C\n" + "D\n");
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }, g -> {
+ try {
+ writeTrashFile("main.cat", "A\n" + "G\n" + "C\n" + "F\n");
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }, g -> {
+ try {
+ writeTrashFile("main.cat", "A\n" + "E\n" + "C\n" + "D\n");
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ })) {
+ // Check that the merge attribute is set to union
+ assertAddMergeAttributeCustom(REFS_HEADS_LEFT, "main.cat", "union");
+ assertAddMergeAttributeCustom(REFS_HEADS_RIGHT, "main.cat",
+ "union");
+
+ checkoutBranch(REFS_HEADS_LEFT);
+ // Merge refs/heads/left -> refs/heads/right
+
+ MergeResult mergeResult = git.merge()
+ .include(git.getRepository().resolve(REFS_HEADS_RIGHT))
+ .call();
+ assertEquals(MergeStatus.MERGED, mergeResult.getMergeStatus());
+
+ // Check that the file is the union of both branches (no conflict
+ // marker added)
+ String result = read(writeTrashFile("res.cat",
+ "A\n" + "G\n" + "E\n" + "C\n" + "F\n"));
+ assertEquals(result, read(git.getRepository().getWorkTree().toPath()
+ .resolve("main.cat").toFile()));
+ }
+ }
+
+ @Test
public void mergeBinaryFile_NoAttr_Conflict() throws IllegalStateException,
IOException, NoHeadException, ConcurrentRefUpdateException,
CheckoutConflictException, InvalidMergeHeadsException,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameGeneratorCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameGeneratorCacheTest.java
new file mode 100644
index 0000000000..65cac11982
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameGeneratorCacheTest.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2025, Google LLC.
+ *
+ * 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.blame;
+
+import static java.lang.String.join;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.eclipse.jgit.blame.cache.BlameCache;
+import org.eclipse.jgit.blame.cache.CacheRegion;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+public class BlameGeneratorCacheTest extends RepositoryTestCase {
+ private static final String FILE = "file.txt";
+
+ /**
+ * Simple history:
+ *
+ * <pre>
+ * C1 C2 C3 C4 C4 blame
+ * lines ----------------------------------
+ * L1 | C1 C1 C1 C1 C1
+ * L2 | C1 C1 *C3 *C4 C4
+ * L3 | C1 C1 *C3 C3 C3
+ * L4 | *C2 C2 *C4 C4
+ * </pre>
+ *
+ * @throws Exception any error
+ */
+ @Test
+ public void blame_simple_correctRegions() throws Exception {
+ RevCommit c1, c2, c3, c4;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ c1 = commit(r, lines("L1C1", "L2C1", "L3C1"));
+ c2 = commit(r, lines("L1C1", "L2C1", "L3C1", "L4C2"), c1);
+ c3 = commit(r, lines("L1C1", "L2C3", "L3C3", "L4C2"), c2);
+ c4 = commit(r, lines("L1C1", "L2C4", "L3C3", "L4C4"), c3);
+ }
+
+ List<EmittedRegion> expectedRegions = Arrays.asList(
+ new EmittedRegion(c1, 0, 1),
+ new EmittedRegion(c4, 1, 2),
+ new EmittedRegion(c3, 2, 3),
+ new EmittedRegion(c4, 3, 4));
+
+ assertRegions(c4, null, expectedRegions, 4);
+ assertRegions(c4, emptyCache(), expectedRegions, 4);
+ assertRegions(c4, blameAndCache(c4), expectedRegions, 4);
+ assertRegions(c4, blameAndCache(c3), expectedRegions, 4);
+ assertRegions(c4, blameAndCache(c2), expectedRegions, 4);
+ assertRegions(c4, blameAndCache(c1), expectedRegions, 4);
+ }
+
+ @Test
+ public void blame_simple_cacheUsage() throws Exception {
+ RevCommit c1, c2, c3, c4;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ c1 = commit(r, lines("L1C1", "L2C1", "L3C1"));
+ c2 = commit(r, lines("L1C1", "L2C1", "L3C1", "L4C2"), c1);
+ c3 = commit(r, lines("L1C1", "L2C3", "L3C3", "L4C2"), c2);
+ c4 = commit(r, lines("L1C1", "L2C4", "L3C3", "L4C4"), c3);
+ }
+
+ assertCacheUsage(c4, null, false, 4);
+ assertCacheUsage(c4, emptyCache(), false, 4);
+ assertCacheUsage(c4, blameAndCache(c4), true, 1);
+ assertCacheUsage(c4, blameAndCache(c3), true, 2);
+ assertCacheUsage(c4, blameAndCache(c2), true, 3);
+ assertCacheUsage(c4, blameAndCache(c1), true, 4);
+ }
+
+ /**
+ * Overwrite:
+ *
+ * <pre>
+ * C1 C2 C3 C3 blame
+ * lines ----------------------------------
+ * L1 | C1 C1 *C3 C3
+ * L2 | C1 C1 *C3 C3
+ * L3 | C1 C1 *C3 C3
+ * L4 | *C2
+ * </pre>
+ *
+ * @throws Exception any error
+ */
+ @Test
+ public void blame_ovewrite_correctRegions() throws Exception {
+ RevCommit c1, c2, c3;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ c1 = commit(r, lines("L1C1", "L2C1", "L3C1"));
+ c2 = commit(r, lines("L1C1", "L2C1", "L3C1", "L4C2"), c1);
+ c3 = commit(r, lines("L1C3", "L2C3", "L3C3"), c2);
+ }
+
+ List<EmittedRegion> expectedRegions = Arrays.asList(
+ new EmittedRegion(c3, 0, 3));
+
+ assertRegions(c3, null, expectedRegions, 3);
+ assertRegions(c3, emptyCache(), expectedRegions, 3);
+ assertRegions(c3, blameAndCache(c3), expectedRegions, 3);
+ assertRegions(c3, blameAndCache(c2), expectedRegions, 3);
+ assertRegions(c3, blameAndCache(c1), expectedRegions, 3);
+ }
+
+ @Test
+ public void blame_overwrite_cacheUsage() throws Exception {
+ RevCommit c1, c2, c3;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ c1 = commit(r, lines("L1C1", "L2C1", "L3C1"));
+ c2 = commit(r, lines("L1C1", "L2C1", "L3C1", "L4C2"), c1);
+ c3 = commit(r, lines("L1C3", "L2C3", "L3C3"), c2);
+ }
+
+ assertCacheUsage(c3, null, false, 1);
+ assertCacheUsage(c3, emptyCache(), false, 1);
+ assertCacheUsage(c3, blameAndCache(c3), true, 1);
+ assertCacheUsage(c3, blameAndCache(c2), false, 1);
+ assertCacheUsage(c3, blameAndCache(c1), false, 1);
+ }
+
+ /**
+ * Merge:
+ *
+ * <pre>
+ * root
+ * ----
+ * L1 -
+ * L2 -
+ * L3 -
+ * / \
+ * sideA sideB
+ * ----- -----
+ * *L1 a L1 -
+ * *L2 a L2 -
+ * *L3 a L3 -
+ * *L4 a *L4 b
+ * L5 - *L5 b
+ * L6 - *L6 b
+ * L7 - *L7 b
+ * \ /
+ * merge
+ * -----
+ * L1-L4 a (from sideA)
+ * L5-L7 - (common, from root)
+ * L8-L11 b (from sideB)
+ * </pre>
+ *
+ * @throws Exception any error
+ */
+ @Test
+ public void blame_merge_correctRegions() throws Exception {
+ RevCommit root, sideA, sideB, mergedTip;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ root = commitAsLines(r, "---");
+ sideA = commitAsLines(r, "aaaa---", root);
+ sideB = commitAsLines(r, "---bbbb", root);
+ mergedTip = commitAsLines(r, "aaaa---bbbb", sideA, sideB);
+ }
+
+ List<EmittedRegion> expectedRegions = Arrays.asList(
+ new EmittedRegion(sideA, 0, 4),
+ new EmittedRegion(root, 4, 7),
+ new EmittedRegion(sideB, 7, 11));
+
+ assertRegions(mergedTip, null, expectedRegions, 11);
+ assertRegions(mergedTip, emptyCache(), expectedRegions, 11);
+ assertRegions(mergedTip, blameAndCache(root), expectedRegions, 11);
+ assertRegions(mergedTip, blameAndCache(sideA), expectedRegions, 11);
+ assertRegions(mergedTip, blameAndCache(sideB), expectedRegions, 11);
+ assertRegions(mergedTip, blameAndCache(mergedTip), expectedRegions, 11);
+ }
+
+ @Test
+ public void blame_merge_cacheUsage() throws Exception {
+ RevCommit root, sideA, sideB, mergedTip;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ root = commitAsLines(r, "---");
+ sideA = commitAsLines(r, "aaaa---", root);
+ sideB = commitAsLines(r, "---bbbb", root);
+ mergedTip = commitAsLines(r, "aaaa---bbbb", sideA, sideB);
+ }
+
+ assertCacheUsage(mergedTip, null, /* cacheUsed */ false,
+ /* candidates */ 4);
+ assertCacheUsage(mergedTip, emptyCache(), false, 4);
+ assertCacheUsage(mergedTip, blameAndCache(mergedTip), true, 1);
+
+ // While splitting unblamed regions to parents, sideA comes first
+ // and gets "aaaa----". Processing is by commit time, so sideB is
+ // explored first
+ assertCacheUsage(mergedTip, blameAndCache(sideA), true, 3);
+ assertCacheUsage(mergedTip, blameAndCache(sideB), true, 4);
+ assertCacheUsage(mergedTip, blameAndCache(root), true, 4);
+ }
+
+ /**
+ * Moving block (insertion)
+ *
+ * <pre>
+ * C1 C2 C3 C3 blame
+ * lines ----------------------------------
+ * L1 | C1 C1 C1 C1
+ * L2 | C1 *C2 C2 C2
+ * L3 | C1 *C3 C3
+ * L4 | C1 C1
+ * </pre>
+ *
+ * @throws Exception any error
+ */
+ @Test
+ public void blame_movingBlock_correctRegions() throws Exception {
+ RevCommit c1, c2, c3;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ c1 = commit(r, lines("L1C1", "L2C1"));
+ c2 = commit(r, lines("L1C1", "middle", "L2C1"), c1);
+ c3 = commit(r, lines("L1C1", "middle", "extra", "L2C1"), c2);
+ }
+
+ List<EmittedRegion> expectedRegions = Arrays.asList(
+ new EmittedRegion(c1, 0, 1),
+ new EmittedRegion(c2, 1, 2),
+ new EmittedRegion(c3, 2, 3),
+ new EmittedRegion(c1, 3, 4));
+
+ assertRegions(c3, null, expectedRegions, 4);
+ assertRegions(c3, emptyCache(), expectedRegions, 4);
+ assertRegions(c3, blameAndCache(c3), expectedRegions, 4);
+ assertRegions(c3, blameAndCache(c2), expectedRegions, 4);
+ assertRegions(c3, blameAndCache(c1), expectedRegions, 4);
+ }
+
+ @Test
+ public void blame_movingBlock_cacheUsage() throws Exception {
+ RevCommit c1, c2, c3;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ c1 = commitAsLines(r, "root---");
+ c2 = commitAsLines(r, "rootXXX---", c1);
+ c3 = commitAsLines(r, "rootYYYXXX---", c2);
+ }
+
+ assertCacheUsage(c3, null, false, 3);
+ assertCacheUsage(c3, emptyCache(), false, 3);
+ assertCacheUsage(c3, blameAndCache(c3), true, 1);
+ assertCacheUsage(c3, blameAndCache(c2), true, 2);
+ assertCacheUsage(c3, blameAndCache(c1), true, 3);
+ }
+
+ private void assertRegions(RevCommit commit, InMemoryBlameCache cache,
+ List<EmittedRegion> expectedRegions, int resultLineCount)
+ throws IOException {
+ try (BlameGenerator gen = new BlameGenerator(db, FILE, cache)) {
+ gen.push(null, db.parseCommit(commit));
+ List<EmittedRegion> regions = consume(gen);
+ assertRegionsEquals(expectedRegions, regions);
+ assertAllLinesCovered(/* lines= */ resultLineCount, regions);
+ }
+ }
+
+ private void assertCacheUsage(RevCommit commit, InMemoryBlameCache cache,
+ boolean useCache, int candidatesVisited) throws IOException {
+ try (BlameGenerator gen = new BlameGenerator(db, FILE, cache)) {
+ gen.push(null, db.parseCommit(commit));
+ consume(gen);
+ assertEquals(useCache, gen.getStats().isCacheHit());
+ assertEquals(candidatesVisited,
+ gen.getStats().getCandidatesVisited());
+ }
+ }
+
+ private static void assertAllLinesCovered(int lines,
+ List<EmittedRegion> regions) {
+ Collections.sort(regions);
+ assertEquals("Starts in first line", 0, regions.get(0).resultStart());
+ for (int i = 1; i < regions.size(); i++) {
+ assertEquals("No gaps", regions.get(i).resultStart(),
+ regions.get(i - 1).resultEnd());
+ }
+ assertEquals("Ends in last line", lines,
+ regions.get(regions.size() - 1).resultEnd());
+ }
+
+ private static void assertRegionsEquals(
+ List<EmittedRegion> expected, List<EmittedRegion> actual) {
+ assertEquals(expected.size(), actual.size());
+ Collections.sort(actual);
+ for (int i = 0; i < expected.size(); i++) {
+ assertEquals(String.format("List differ in element %d", i),
+ expected.get(i), actual.get(i));
+ }
+ }
+
+ private static InMemoryBlameCache emptyCache() {
+ return new InMemoryBlameCache("<empty>");
+ }
+
+ private List<EmittedRegion> consume(BlameGenerator generator)
+ throws IOException {
+ List<EmittedRegion> result = new ArrayList<>();
+ while (generator.next()) {
+ EmittedRegion genRegion = new EmittedRegion(
+ generator.getSourceCommit().toObjectId(),
+ generator.getResultStart(), generator.getResultEnd());
+ result.add(genRegion);
+ }
+ return result;
+ }
+
+ private InMemoryBlameCache blameAndCache(RevCommit commit)
+ throws IOException {
+ List<CacheRegion> regions;
+ try (BlameGenerator generator = new BlameGenerator(db, FILE)) {
+ generator.push(null, commit);
+ regions = consume(generator).stream()
+ .map(EmittedRegion::asCacheRegion)
+ .collect(Collectors.toUnmodifiableList());
+ }
+ InMemoryBlameCache cache = new InMemoryBlameCache("<x>");
+ cache.put(commit, FILE, regions);
+ return cache;
+ }
+
+ private static RevCommit commitAsLines(TestRepository<?> r,
+ String charPerLine, RevCommit... parents) throws Exception {
+ return commit(r, charPerLine.replaceAll("\\S", "$0\n"), parents);
+ }
+
+ private static RevCommit commit(TestRepository<?> r, String contents,
+ RevCommit... parents) throws Exception {
+ return r.commit(r.tree(r.file(FILE, r.blob(contents))), parents);
+ }
+
+ private static String lines(String... l) {
+ return join("\n", l);
+ }
+
+ private record EmittedRegion(ObjectId oid, int resultStart, int resultEnd)
+ implements Comparable<EmittedRegion> {
+ @Override
+ public int compareTo(EmittedRegion o) {
+ return resultStart - o.resultStart;
+ }
+
+ CacheRegion asCacheRegion() {
+ return new CacheRegion(FILE, oid, resultStart, resultEnd);
+ }
+ }
+
+ private static class InMemoryBlameCache implements BlameCache {
+
+ private final Map<Key, List<CacheRegion>> cache = new HashMap<>();
+
+ private final String description;
+
+ public InMemoryBlameCache(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public List<CacheRegion> get(Repository repo, ObjectId commitId,
+ String path) throws IOException {
+ return cache.get(new Key(commitId.name(), path));
+ }
+
+ public void put(ObjectId commitId, String path,
+ List<CacheRegion> cachedRegions) {
+ cache.put(new Key(commitId.name(), path), cachedRegions);
+ }
+
+ @Override
+ public String toString() {
+ return "InMemoryCache: " + description;
+ }
+
+ record Key(String commitId, String path) {
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameRegionMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameRegionMergerTest.java
new file mode 100644
index 0000000000..1b28676fbf
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameRegionMergerTest.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2025, Google LLC.
+ *
+ * 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.blame;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.eclipse.jgit.blame.cache.CacheRegion;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+public class BlameRegionMergerTest extends RepositoryTestCase {
+
+ private static final ObjectId O1 = ObjectId
+ .fromString("ff6dd8db6edc9aa0ac58fea1d14a55be46c3eb14");
+
+ private static final ObjectId O2 = ObjectId
+ .fromString("c3c7f680c6bee238617f25f6aa85d0b565fc8ecb");
+
+ private static final ObjectId O3 = ObjectId
+ .fromString("29e014aad0399fe8ede7c101d01b6e440ac9966b");
+
+ List<RevCommit> fakeCommits = List.of(new FakeRevCommit(O1),
+ new FakeRevCommit(O2), new FakeRevCommit(O3));
+
+ // In reverse order, so the code doesn't assume a sorted list
+ List<CacheRegion> cachedRegions = List.of(
+ new CacheRegion("README", O3, 20, 30),
+ new CacheRegion("README", O2, 10, 20),
+ new CacheRegion("README", O1, 0, 10));
+
+ BlameRegionMerger blamer = new BlameRegionMergerFakeCommits(fakeCommits,
+ cachedRegions);
+
+ @Test
+ public void intersectRegions_allInside() {
+ Region unblamed = new Region(15, 18, 10);
+ CacheRegion blamed = new CacheRegion("README", O1, 10, 90);
+
+ Region result = BlameRegionMerger.intersectRegions(unblamed, blamed);
+ // Same lines in result and source
+ assertEquals(15, result.resultStart);
+ assertEquals(18, result.sourceStart);
+ assertEquals(10, result.length);
+ assertNull(result.next);
+ }
+
+ @Test
+ public void intersectRegions_startsBefore() {
+ // Intesecting [4, 14) with [10, 90)
+ Region unblamed = new Region(30, 4, 10);
+ CacheRegion blamed = new CacheRegion("README", O1, 10, 90);
+
+ Region result = BlameRegionMerger.intersectRegions(unblamed, blamed);
+
+ // The unblamed region starting at 4 (sourceStart), starts at 30 in the
+ // original file (resultStart). e.g. some commit introduced
+ // lines. If we take the second portion of the region, we need to move
+ // the result start accordingly.
+ assertEquals(36, result.resultStart);
+ assertEquals(10, result.sourceStart);
+ assertEquals(4, result.length);
+ assertNull(result.next);
+ }
+
+ @Test
+ public void intersectRegions_endsAfter() {
+ // Intesecting [85, 95) with [10, 90)
+ Region unblamed = new Region(30, 85, 10);
+ CacheRegion blamed = new CacheRegion("README", O1, 10, 90);
+
+ Region result = BlameRegionMerger.intersectRegions(unblamed, blamed);
+
+ assertEquals(30, result.resultStart);
+ assertEquals(85, result.sourceStart);
+ assertEquals(5, result.length);
+ assertNull(result.next);
+ }
+
+ @Test
+ public void intersectRegions_spillOverBothSides() {
+ // Intesecting [5, 100) with [10, 90)
+ Region unblamed = new Region(30, 5, 95);
+ CacheRegion blamed = new CacheRegion("README", O1, 10, 90);
+
+ Region result = BlameRegionMerger.intersectRegions(unblamed, blamed);
+
+ assertEquals(35, result.resultStart);
+ assertEquals(10, result.sourceStart);
+ assertEquals(80, result.length);
+ assertNull(result.next);
+ }
+
+ @Test
+ public void intersectRegions_exactMatch() {
+ // Intesecting [5, 100) with [10, 90)
+ Region unblamed = new Region(30, 10, 80);
+ CacheRegion blamed = new CacheRegion("README", O1, 10, 90);
+
+ Region result = BlameRegionMerger.intersectRegions(unblamed, blamed);
+
+ assertEquals(30, result.resultStart);
+ assertEquals(10, result.sourceStart);
+ assertEquals(80, result.length);
+ assertNull(result.next);
+ }
+
+ @Test
+ public void findOverlaps_allInside() {
+ Region unblamed = new Region(0, 11, 4);
+ List<CacheRegion> overlaps = blamer.findOverlaps(unblamed);
+ assertEquals(1, overlaps.size());
+ assertEquals(10, overlaps.get(0).getStart());
+ assertEquals(20, overlaps.get(0).getEnd());
+ }
+
+ @Test
+ public void findOverlaps_overTwoRegions() {
+ Region unblamed = new Region(0, 8, 4);
+ List<CacheRegion> overlaps = blamer.findOverlaps(unblamed);
+ assertEquals(2, overlaps.size());
+ assertEquals(0, overlaps.get(0).getStart());
+ assertEquals(10, overlaps.get(0).getEnd());
+ assertEquals(10, overlaps.get(1).getStart());
+ assertEquals(20, overlaps.get(1).getEnd());
+ }
+
+ @Test
+ public void findOverlaps_overThreeRegions() {
+ Region unblamed = new Region(0, 8, 15);
+ List<CacheRegion> overlaps = blamer.findOverlaps(unblamed);
+ assertEquals(3, overlaps.size());
+ assertEquals(0, overlaps.get(0).getStart());
+ assertEquals(10, overlaps.get(0).getEnd());
+ assertEquals(10, overlaps.get(1).getStart());
+ assertEquals(20, overlaps.get(1).getEnd());
+ assertEquals(20, overlaps.get(2).getStart());
+ assertEquals(30, overlaps.get(2).getEnd());
+ }
+
+ @Test
+ public void blame_exactOverlap() throws IOException {
+ Region unblamed = new Region(0, 10, 10);
+ List<Candidate> blamed = blamer.mergeOneRegion(unblamed);
+
+ assertEquals(1, blamed.size());
+ Candidate c = blamed.get(0);
+ assertEquals(c.sourceCommit.name(), O2.name());
+ assertEquals(c.regionList.resultStart, unblamed.resultStart);
+ assertEquals(c.regionList.sourceStart, unblamed.sourceStart);
+ assertEquals(10, c.regionList.length);
+ assertNull(c.regionList.next);
+ }
+
+ @Test
+ public void blame_corruptedIndex() {
+ Region outOfRange = new Region(0, 43, 4);
+ // This region is out of the blamed area
+ assertThrows(IOException.class,
+ () -> blamer.mergeOneRegion(outOfRange));
+ }
+
+ @Test
+ public void blame_allInsideOneBlamedRegion() throws IOException {
+ Region unblamed = new Region(0, 5, 3);
+ // This region if fully blamed to O1
+ List<Candidate> blamed = blamer.mergeOneRegion(unblamed);
+ assertEquals(1, blamed.size());
+ Candidate c = blamed.get(0);
+ assertEquals(c.sourceCommit.name(), O1.name());
+ assertEquals(c.regionList.resultStart, unblamed.resultStart);
+ assertEquals(c.regionList.sourceStart, unblamed.sourceStart);
+ assertEquals(3, c.regionList.length);
+ assertNull(c.regionList.next);
+ }
+
+ @Test
+ public void blame_overTwoBlamedRegions() throws IOException {
+ Region unblamed = new Region(0, 8, 5);
+ // (8, 10) belongs go C1, (10, 13) to C2
+ List<Candidate> blamed = blamer.mergeOneRegion(unblamed);
+ assertEquals(2, blamed.size());
+ Candidate c = blamed.get(0);
+ assertEquals(c.sourceCommit.name(), O1.name());
+ assertEquals(unblamed.resultStart, c.regionList.resultStart);
+ assertEquals(unblamed.sourceStart, c.regionList.sourceStart);
+ assertEquals(2, c.regionList.length);
+ assertNull(c.regionList.next);
+
+ c = blamed.get(1);
+ assertEquals(c.sourceCommit.name(), O2.name());
+ assertEquals(2, c.regionList.resultStart);
+ assertEquals(10, c.regionList.sourceStart);
+ assertEquals(3, c.regionList.length);
+ assertNull(c.regionList.next);
+ }
+
+ @Test
+ public void blame_all() throws IOException {
+ Region unblamed = new Region(0, 0, 30);
+ List<Candidate> blamed = blamer.mergeOneRegion(unblamed);
+ assertEquals(3, blamed.size());
+ Candidate c = blamed.get(0);
+ assertEquals(c.sourceCommit.name(), O1.name());
+ assertEquals(unblamed.resultStart, c.regionList.resultStart);
+ assertEquals(unblamed.sourceStart, c.regionList.sourceStart);
+ assertEquals(10, c.regionList.length);
+ assertNull(c.regionList.next);
+
+ c = blamed.get(1);
+ assertEquals(c.sourceCommit.name(), O2.name());
+ assertEquals(10, c.regionList.resultStart);
+ assertEquals(10, c.regionList.sourceStart);
+ assertEquals(10, c.regionList.length);
+ assertNull(c.regionList.next);
+
+ c = blamed.get(2);
+ assertEquals(c.sourceCommit.name(), O3.name());
+ assertEquals(20, c.regionList.resultStart);
+ assertEquals(20, c.regionList.sourceStart);
+ assertEquals(10, c.regionList.length);
+ assertNull(c.regionList.next);
+ }
+
+ @Test
+ public void blame_fromCandidate() {
+ // We don't use anything from the candidate besides the
+ // regionList
+ Candidate c = new Candidate(null, null, null);
+ c.regionList = new Region(0, 8, 5);
+ c.regionList.next = new Region(22, 22, 4);
+
+ Candidate blamed = blamer.mergeCandidate(c);
+ // Three candidates
+ assertNotNull(blamed);
+ assertNotNull(blamed.queueNext);
+ assertNotNull(blamed.queueNext.queueNext);
+ assertNull(blamed.queueNext.queueNext.queueNext);
+
+ assertEquals(O1.name(), blamed.sourceCommit.name());
+
+ Candidate second = blamed.queueNext;
+ assertEquals(O2.name(), second.sourceCommit.name());
+
+ Candidate third = blamed.queueNext.queueNext;
+ assertEquals(O3.name(), third.sourceCommit.name());
+ }
+
+ @Test
+ public void blame_fromCandidate_twiceCandidateInOutput() {
+ Candidate c = new Candidate(null, null, null);
+ // This produces O1 and O2
+ c.regionList = new Region(0, 8, 5);
+ // This produces O2 and O3
+ c.regionList.next = new Region(20, 15, 7);
+
+ Candidate blamed = blamer.mergeCandidate(c);
+ assertCandidateSingleRegion(O1, 2, blamed);
+ blamed = blamed.queueNext;
+ assertCandidateSingleRegion(O2, 3, blamed);
+ // We do not merge candidates afterwards, so these are
+ // two different candidates to the same source
+ blamed = blamed.queueNext;
+ assertCandidateSingleRegion(O2, 5, blamed);
+ blamed = blamed.queueNext;
+ assertCandidateSingleRegion(O3, 2, blamed);
+ assertNull(blamed.queueNext);
+ }
+
+ private static void assertCandidateSingleRegion(ObjectId expectedOid,
+ int expectedLength, Candidate actual) {
+ assertNotNull("candidate", actual);
+ assertNotNull("region list not empty", actual.regionList);
+ assertNull("region list has only one element", actual.regionList.next);
+ assertEquals(expectedOid, actual.sourceCommit);
+ assertEquals(expectedLength, actual.regionList.length);
+ }
+
+ private static final class BlameRegionMergerFakeCommits
+ extends BlameRegionMerger {
+
+ private final Map<ObjectId, RevCommit> cache;
+
+ BlameRegionMergerFakeCommits(List<RevCommit> commits,
+ List<CacheRegion> blamedRegions) {
+ super(null, null, blamedRegions);
+ cache = commits.stream().collect(Collectors
+ .toMap(RevCommit::toObjectId, Function.identity()));
+ }
+
+ @Override
+ protected RevCommit parse(ObjectId oid) {
+ return cache.get(oid);
+ }
+ }
+
+ private static final class FakeRevCommit extends RevCommit {
+ FakeRevCommit(AnyObjectId id) {
+ super(id);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterBuiltInDriverTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterBuiltInDriverTest.java
new file mode 100644
index 0000000000..1352871983
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterBuiltInDriverTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.diff;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.junit.Test;
+
+public class DiffFormatterBuiltInDriverTest extends RepositoryTestCase {
+ @Test
+ public void testCppDriver() throws Exception {
+ String fileName = "greeting.c";
+ String body = Files.readString(
+ Path.of(JGitTestUtil.getTestResourceFile(fileName)
+ .getAbsolutePath()));
+ RevCommit c1;
+ RevCommit c2;
+ try (Git git = new Git(db)) {
+ createCommit(git, ".gitattributes", "*.c diff=cpp");
+ c1 = createCommit(git, fileName, body);
+ c2 = createCommit(git, fileName,
+ body.replace("Good day", "Greetings")
+ .replace("baz", "qux"));
+ }
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter diffFormatter = new DiffFormatter(os)) {
+ String actual = getHunkHeaders(c1, c2, os, diffFormatter);
+ String expected =
+ "@@ -27,7 +27,7 @@ void getPersonalizedGreeting(char *result, const char *name, const char *timeOfD\n"
+ + "@@ -37,7 +37,7 @@ int main() {";
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testDtsDriver() throws Exception {
+ String fileName = "sample.dtsi";
+ String body = Files.readString(
+ Path.of(JGitTestUtil.getTestResourceFile(fileName)
+ .getAbsolutePath()));
+ RevCommit c1;
+ RevCommit c2;
+ try (Git git = new Git(db)) {
+ createCommit(git, ".gitattributes", "*.dtsi diff=dts");
+ c1 = createCommit(git, fileName, body);
+ c2 = createCommit(git, fileName,
+ body.replace("clock-frequency = <24000000>",
+ "clock-frequency = <48000000>"));
+ }
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter diffFormatter = new DiffFormatter(os)) {
+ String actual = getHunkHeaders(c1, c2, os, diffFormatter);
+ String expected = "@@ -20,6 +20,6 @@ uart0: uart@101f1000 {";
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testJavaDriver() throws Exception {
+ String resourceName = "greeting.javasource";
+ String body = Files.readString(
+ Path.of(JGitTestUtil.getTestResourceFile(resourceName)
+ .getAbsolutePath()));
+ RevCommit c1;
+ RevCommit c2;
+ try (Git git = new Git(db)) {
+ createCommit(git, ".gitattributes", "*.java diff=java");
+ String fileName = "Greeting.java";
+ c1 = createCommit(git, fileName, body);
+ c2 = createCommit(git, fileName,
+ body.replace("Good day", "Greetings")
+ .replace("baz", "qux"));
+ }
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter diffFormatter = new DiffFormatter(os)) {
+ String actual = getHunkHeaders(c1, c2, os, diffFormatter);
+ String expected =
+ "@@ -22,7 +22,7 @@ public String getPersonalizedGreeting(String name, String timeOfDay) {\n"
+ + "@@ -32,6 +32,6 @@ public static void main(String[] args) {";
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testPythonDriver() throws Exception {
+ String fileName = "greeting.py";
+ String body = Files.readString(
+ Path.of(JGitTestUtil.getTestResourceFile(fileName)
+ .getAbsolutePath()));
+ RevCommit c1;
+ RevCommit c2;
+ try (Git git = new Git(db)) {
+ createCommit(git, ".gitattributes", "*.py diff=python");
+ c1 = createCommit(git, fileName, body);
+ c2 = createCommit(git, fileName,
+ body.replace("Good day", "Greetings"));
+ }
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter diffFormatter = new DiffFormatter(os)) {
+ String actual = getHunkHeaders(c1, c2, os, diffFormatter);
+ String expected = "@@ -16,7 +16,7 @@ def get_personalized_greeting(self, name, time_of_day):";
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testRustDriver() throws Exception {
+ String fileName = "greeting.rs";
+ String body = Files.readString(
+ Path.of(JGitTestUtil.getTestResourceFile(fileName)
+ .getAbsolutePath()));
+ RevCommit c1;
+ RevCommit c2;
+ try (Git git = new Git(db)) {
+ createCommit(git, ".gitattributes", "*.rs diff=rust");
+ c1 = createCommit(git, fileName, body);
+ c2 = createCommit(git, fileName,
+ body.replace("Good day", "Greetings")
+ .replace("baz", "qux"));
+ }
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter diffFormatter = new DiffFormatter(os)) {
+ String actual = getHunkHeaders(c1, c2, os, diffFormatter);
+ String expected =
+ "@@ -14,7 +14,7 @@ fn get_personalized_greeting(&self, name: &str, time_of_day: &str) -> String {\n"
+ + "@@ -23,5 +23,5 @@ fn main() {";
+ assertEquals(expected, actual);
+ }
+ }
+
+ private String getHunkHeaders(RevCommit c1, RevCommit c2,
+ ByteArrayOutputStream os, DiffFormatter diffFormatter)
+ throws IOException {
+ diffFormatter.setRepository(db);
+ diffFormatter.format(new CanonicalTreeParser(null, db.newObjectReader(),
+ c1.getTree()),
+ new CanonicalTreeParser(null, db.newObjectReader(),
+ c2.getTree()));
+ diffFormatter.flush();
+ return Arrays.stream(os.toString(StandardCharsets.UTF_8).split("\n"))
+ .filter(line -> line.startsWith("@@"))
+ .collect(Collectors.joining("\n"));
+ }
+
+ private RevCommit createCommit(Git git, String fileName, String body)
+ throws IOException, GitAPIException {
+ writeTrashFile(fileName, body);
+ git.add().addFilepattern(".").call();
+ return git.commit().setMessage("message").call();
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/BareSuperprojectWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/BareSuperprojectWriterTest.java
index c3b93879b2..5065b57840 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/BareSuperprojectWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/BareSuperprojectWriterTest.java
@@ -12,6 +12,7 @@ package org.eclipse.jgit.gitrepo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -22,6 +23,7 @@ import java.util.List;
import org.eclipse.jgit.gitrepo.BareSuperprojectWriter.BareWriterConfig;
import org.eclipse.jgit.gitrepo.RepoCommand.RemoteReader;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
@@ -68,6 +70,49 @@ public class BareSuperprojectWriterTest extends RepositoryTestCase {
}
@Test
+ public void write_setGitModulesContents_pinned() throws Exception {
+ try (Repository bareRepo = createBareRepository()) {
+ RepoProject pinWithUpstream = new RepoProject("pinWithUpstream",
+ "path/x", "cbc0fae7e1911d27e1de37d364698dba4411c78b",
+ "remote", "");
+ pinWithUpstream.setUrl("http://example.com/a");
+ pinWithUpstream.setUpstream("branchX");
+
+ RepoProject pinWithoutUpstream = new RepoProject(
+ "pinWithoutUpstream", "path/y",
+ "cbc0fae7e1911d27e1de37d364698dba4411c78b", "remote", "");
+ pinWithoutUpstream.setUrl("http://example.com/b");
+
+ RemoteReader mockRemoteReader = mock(RemoteReader.class);
+
+ BareSuperprojectWriter w = new BareSuperprojectWriter(bareRepo,
+ null, "refs/heads/master", author, mockRemoteReader,
+ BareWriterConfig.getDefault(), List.of());
+
+ RevCommit commit = w
+ .write(Arrays.asList(pinWithUpstream, pinWithoutUpstream));
+
+ String contents = readContents(bareRepo, commit, ".gitmodules");
+ Config cfg = new Config();
+ cfg.fromText(contents);
+
+ assertThat(cfg.getString("submodule", "pinWithUpstream", "path"),
+ is("path/x"));
+ assertThat(cfg.getString("submodule", "pinWithUpstream", "url"),
+ is("http://example.com/a"));
+ assertThat(cfg.getString("submodule", "pinWithUpstream", "ref"),
+ is("branchX"));
+
+ assertThat(cfg.getString("submodule", "pinWithoutUpstream", "path"),
+ is("path/y"));
+ assertThat(cfg.getString("submodule", "pinWithoutUpstream", "url"),
+ is("http://example.com/b"));
+ assertThat(cfg.getString("submodule", "pinWithoutUpstream", "ref"),
+ nullValue());
+ }
+ }
+
+ @Test
public void write_setExtraContents() throws Exception {
try (Repository bareRepo = createBareRepository()) {
RepoProject repoProject = new RepoProject("subprojectX", "path/to",
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java
index 20958a812c..fca27d32aa 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java
@@ -11,6 +11,7 @@ package org.eclipse.jgit.gitrepo;
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 static org.junit.Assert.fail;
@@ -18,7 +19,9 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -138,6 +141,72 @@ public class ManifestParserTest {
.collect(Collectors.toSet()));
}
+ @Test
+ public void testPinProjectWithUpstream() throws Exception {
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"pin-with-upstream\"")
+ .append(" revision=\"9b2fe85c0279f4d5ac69f07ddcd48566c3555405\"")
+ .append(" upstream=\"branchX\"/>")
+ .append("<project path=\"bar\" name=\"pin-without-upstream\"")
+ .append(" revision=\"76ce6d91a2e07fdfcbfc8df6970c9e98a98e36a0\" />")
+ .append("</manifest>");
+
+ ManifestParser parser = new ManifestParser(null, null, "master",
+ "https://git.google.com/", null, null);
+ parser.read(new ByteArrayInputStream(
+ xmlContent.toString().getBytes(UTF_8)));
+
+ Map<String, RepoProject> repos = parser.getProjects().stream().collect(
+ Collectors.toMap(RepoProject::getName, Function.identity()));
+ assertEquals(2, repos.size());
+
+ RepoProject foo = repos.get("pin-with-upstream");
+ assertEquals("pin-with-upstream", foo.getName());
+ assertEquals("9b2fe85c0279f4d5ac69f07ddcd48566c3555405",
+ foo.getRevision());
+ assertEquals("branchX", foo.getUpstream());
+
+ RepoProject bar = repos.get("pin-without-upstream");
+ assertEquals("pin-without-upstream", bar.getName());
+ assertEquals("76ce6d91a2e07fdfcbfc8df6970c9e98a98e36a0",
+ bar.getRevision());
+ assertNull(bar.getUpstream());
+ }
+
+ @Test
+ public void testWithDestBranch() throws Exception {
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"foo\"")
+ .append(" dest-branch=\"branchX\"/>")
+ .append("<project path=\"bar\" name=\"bar\"/>")
+ .append("</manifest>");
+
+ ManifestParser parser = new ManifestParser(null, null, "master",
+ "https://git.google.com/", null, null);
+ parser.read(new ByteArrayInputStream(
+ xmlContent.toString().getBytes(UTF_8)));
+
+ Map<String, RepoProject> repos = parser.getProjects().stream().collect(
+ Collectors.toMap(RepoProject::getName, Function.identity()));
+ assertEquals(2, repos.size());
+
+ RepoProject foo = repos.get("foo");
+ assertEquals("foo", foo.getName());
+ assertEquals("branchX", foo.getDestBranch());
+
+ RepoProject bar = repos.get("bar");
+ assertEquals("bar", bar.getName());
+ assertNull(bar.getDestBranch());
+ }
+
void testNormalize(String in, String want) {
URI got = ManifestParser.normalizeEmptyPath(URI.create(in));
if (!got.toString().equals(want)) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
index ca6f2e1053..3162e7910b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
@@ -1171,6 +1171,94 @@ public class RepoCommandTest extends RepositoryTestCase {
}
}
+ @Test
+ public void testRecordRemoteBranch_pinned() throws Exception {
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository();
+
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"pin-noupstream\"")
+ .append(" name=\"pin-noupstream\"")
+ .append(" revision=\"76ce6d91a2e07fdfcbfc8df6970c9e98a98e36a0\" />")
+ .append("<project path=\"pin-upstream\"")
+ .append(" name=\"pin-upstream\"")
+ .append(" upstream=\"branchX\"")
+ .append(" revision=\"76ce6d91a2e07fdfcbfc8df6970c9e98a98e36a0\" />")
+ .append("</manifest>");
+ JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+ xmlContent.toString());
+
+ RepoCommand command = new RepoCommand(remoteDb);
+ command.setPath(
+ tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri).setRecordRemoteBranch(true).call();
+ // Clone it
+ File directory = createTempDirectory("testBareRepo");
+ try (Repository localDb = Git.cloneRepository().setDirectory(directory)
+ .setURI(remoteDb.getDirectory().toURI().toString()).call()
+ .getRepository();) {
+ // The .gitmodules file should exist
+ File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
+ assertTrue("The .gitmodules file should exist",
+ gitmodules.exists());
+ FileBasedConfig c = new FileBasedConfig(gitmodules, FS.DETECTED);
+ c.load();
+ assertEquals("Pinned submodule with upstream records the ref",
+ "branchX", c.getString("submodule", "pin-upstream", "ref"));
+ assertNull("Pinned submodule without upstream don't have ref",
+ c.getString("submodule", "pin-noupstream", "ref"));
+ }
+ }
+
+ @Test
+ public void testRecordRemoteBranch_pinned_nameConflict() throws Exception {
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository();
+
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"pin-upstream\"")
+ .append(" name=\"pin-upstream\"")
+ .append(" upstream=\"branchX\"")
+ .append(" revision=\"76ce6d91a2e07fdfcbfc8df6970c9e98a98e36a0\" />")
+ .append("<project path=\"pin-upstream-name-conflict\"")
+ .append(" name=\"pin-upstream\"")
+ .append(" upstream=\"branchX\"")
+ .append(" revision=\"76ce6d91a2e07fdfcbfc8df6970c9e98a98e36a0\" />")
+ .append("</manifest>");
+ JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+ xmlContent.toString());
+
+ RepoCommand command = new RepoCommand(remoteDb);
+ command.setPath(
+ tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri).setRecordRemoteBranch(true).call();
+ // Clone it
+ File directory = createTempDirectory("testBareRepo");
+ try (Repository localDb = Git.cloneRepository().setDirectory(directory)
+ .setURI(remoteDb.getDirectory().toURI().toString()).call()
+ .getRepository();) {
+ // The .gitmodules file should exist
+ File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
+ assertTrue("The .gitmodules file should exist",
+ gitmodules.exists());
+ FileBasedConfig c = new FileBasedConfig(gitmodules, FS.DETECTED);
+ c.load();
+ assertEquals("Upstream is preserved in name conflict", "branchX",
+ c.getString("submodule", "pin-upstream/pin-upstream",
+ "ref"));
+ assertEquals("Upstream is preserved in name conflict (other side)",
+ "branchX", c.getString("submodule",
+ "pin-upstream/pin-upstream-name-conflict", "ref"));
+ }
+ }
@Test
public void testRecordSubmoduleLabels() throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java
index 9f65ee2074..80a0f0cea5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java
@@ -10,6 +10,7 @@
package org.eclipse.jgit.internal.storage.commitgraph;
+import static java.util.stream.Collectors.toList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertArrayEquals;
@@ -19,8 +20,12 @@ import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
import java.util.Set;
import org.eclipse.jgit.dircache.DirCacheEntry;
@@ -413,6 +418,27 @@ public class CommitGraphWriterTest extends RepositoryTestCase {
"119,69,63,-8,0,"));
}
+ @Test
+ public void testPathDiffCalculator_skipUnchangedTree() throws Exception {
+ RevCommit root = tr.commit(tr.tree(
+ tr.file("d/sd1/f1", tr.blob("f1")),
+ tr.file("d/sd2/f2", tr.blob("f2"))));
+ RevCommit tip = tr.commit(tr.tree(
+ tr.file("d/sd1/f1", tr.blob("f1")),
+ tr.file("d/sd2/f2", tr.blob("f2B"))), root);
+ CommitGraphWriter.PathDiffCalculator c = new CommitGraphWriter.PathDiffCalculator();
+
+ Optional<HashSet<ByteBuffer>> byteBuffers = c.changedPaths(walk.getObjectReader(), tip);
+
+ assertTrue(byteBuffers.isPresent());
+ List<String> asString = byteBuffers.get().stream()
+ .map(b -> StandardCharsets.UTF_8.decode(b).toString())
+ .collect(toList());
+ assertThat(asString, containsInAnyOrder("d", "d/sd2", "d/sd2/f2"));
+ // We don't walk into d/sd1/f1
+ assertEquals(1, c.stepCounter);
+ }
+
RevCommit commit(RevCommit... parents) throws Exception {
return tr.commit(parents);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStatsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStatsTest.java
new file mode 100644
index 0000000000..2c4b432a01
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStatsTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2024, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheTable.BlockCacheStats;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertArrayEquals;
+
+import java.util.List;
+
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.junit.Test;
+
+public class AggregatedBlockCacheStatsTest {
+ @Test
+ public void getName() {
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of());
+
+ assertThat(aggregatedBlockCacheStats.getName(),
+ equalTo(AggregatedBlockCacheStats.class.getName()));
+ }
+
+ @Test
+ public void getCurrentSize_aggregatesCurrentSizes() {
+ long[] currentSizes = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ packStats.addToLiveBytes(new TestKey(PackExt.PACK), 5);
+ currentSizes[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ bitmapStats.addToLiveBytes(new TestKey(PackExt.BITMAP_INDEX), 6);
+ currentSizes[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ indexStats.addToLiveBytes(new TestKey(PackExt.INDEX), 7);
+ currentSizes[PackExt.INDEX.getPosition()] = 7;
+
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of(packStats, bitmapStats, indexStats));
+
+ assertArrayEquals(aggregatedBlockCacheStats.getCurrentSize(),
+ currentSizes);
+ }
+
+ @Test
+ public void getHitCount_aggregatesHitCounts() {
+ long[] hitCounts = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementHit(new TestKey(PackExt.PACK)));
+ hitCounts[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> bitmapStats
+ .incrementHit(new TestKey(PackExt.BITMAP_INDEX)));
+ hitCounts[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementHit(new TestKey(PackExt.INDEX)));
+ hitCounts[PackExt.INDEX.getPosition()] = 7;
+
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of(packStats, bitmapStats, indexStats));
+
+ assertArrayEquals(aggregatedBlockCacheStats.getHitCount(), hitCounts);
+ }
+
+ @Test
+ public void getMissCount_aggregatesMissCounts() {
+ long[] missCounts = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementMiss(new TestKey(PackExt.PACK)));
+ missCounts[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> bitmapStats
+ .incrementMiss(new TestKey(PackExt.BITMAP_INDEX)));
+ missCounts[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementMiss(new TestKey(PackExt.INDEX)));
+ missCounts[PackExt.INDEX.getPosition()] = 7;
+
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of(packStats, bitmapStats, indexStats));
+
+ assertArrayEquals(aggregatedBlockCacheStats.getMissCount(), missCounts);
+ }
+
+ @Test
+ public void getTotalRequestCount_aggregatesRequestCounts() {
+ long[] totalRequestCounts = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5, () -> {
+ packStats.incrementHit(new TestKey(PackExt.PACK));
+ packStats.incrementMiss(new TestKey(PackExt.PACK));
+ });
+ totalRequestCounts[PackExt.PACK.getPosition()] = 10;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> {
+ bitmapStats.incrementHit(new TestKey(PackExt.BITMAP_INDEX));
+ bitmapStats.incrementMiss(new TestKey(PackExt.BITMAP_INDEX));
+ });
+ totalRequestCounts[PackExt.BITMAP_INDEX.getPosition()] = 12;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7, () -> {
+ indexStats.incrementHit(new TestKey(PackExt.INDEX));
+ indexStats.incrementMiss(new TestKey(PackExt.INDEX));
+ });
+ totalRequestCounts[PackExt.INDEX.getPosition()] = 14;
+
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of(packStats, bitmapStats, indexStats));
+
+ assertArrayEquals(aggregatedBlockCacheStats.getTotalRequestCount(),
+ totalRequestCounts);
+ }
+
+ @Test
+ public void getHitRatio_aggregatesHitRatios() {
+ long[] hitRatios = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementHit(new TestKey(PackExt.PACK)));
+ hitRatios[PackExt.PACK.getPosition()] = 100;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> {
+ bitmapStats.incrementHit(new TestKey(PackExt.BITMAP_INDEX));
+ bitmapStats.incrementMiss(new TestKey(PackExt.BITMAP_INDEX));
+ });
+ hitRatios[PackExt.BITMAP_INDEX.getPosition()] = 50;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementMiss(new TestKey(PackExt.INDEX)));
+ hitRatios[PackExt.INDEX.getPosition()] = 0;
+
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of(packStats, bitmapStats, indexStats));
+
+ assertArrayEquals(aggregatedBlockCacheStats.getHitRatio(), hitRatios);
+ }
+
+ @Test
+ public void getEvictions_aggregatesEvictions() {
+ long[] evictions = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementEvict(new TestKey(PackExt.PACK)));
+ evictions[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> bitmapStats
+ .incrementEvict(new TestKey(PackExt.BITMAP_INDEX)));
+ evictions[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementEvict(new TestKey(PackExt.INDEX)));
+ evictions[PackExt.INDEX.getPosition()] = 7;
+
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of(packStats, bitmapStats, indexStats));
+
+ assertArrayEquals(aggregatedBlockCacheStats.getEvictions(), evictions);
+ }
+
+ private static void incrementCounter(int amount, Runnable fn) {
+ for (int i = 0; i < amount; i++) {
+ fn.run();
+ }
+ }
+
+ private static long[] createEmptyStatsArray() {
+ return new long[PackExt.values().length];
+ }
+
+ private static class TestKey extends DfsStreamKey {
+ TestKey(PackExt packExt) {
+ super(0, packExt);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTableTest.java
new file mode 100644
index 0000000000..2e2f86bf80
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTableTest.java
@@ -0,0 +1,67 @@
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DEFAULT_NAME;
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheTable.BlockCacheStats;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.isA;
+
+import java.util.List;
+
+import org.junit.Test;
+
+public class ClockBlockCacheTableTest {
+ private static final String NAME = "name";
+
+ @Test
+ public void getName_nameNotConfigured_returnsDefaultName() {
+ ClockBlockCacheTable cacheTable = new ClockBlockCacheTable(
+ createBlockCacheConfig());
+
+ assertThat(cacheTable.getName(), equalTo(DEFAULT_NAME));
+ }
+
+ @Test
+ public void getName_nameConfigured_returnsConfiguredName() {
+ ClockBlockCacheTable cacheTable = new ClockBlockCacheTable(
+ createBlockCacheConfig().setName(NAME));
+
+ assertThat(cacheTable.getName(), equalTo(NAME));
+ }
+
+ @Test
+ public void getBlockCacheStats_nameNotConfigured_returnsBlockCacheStatsWithDefaultName() {
+ ClockBlockCacheTable cacheTable = new ClockBlockCacheTable(
+ createBlockCacheConfig());
+
+ assertThat(cacheTable.getBlockCacheStats(), hasSize(1));
+ assertThat(cacheTable.getBlockCacheStats().get(0).getName(),
+ equalTo(DEFAULT_NAME));
+ }
+
+ @Test
+ public void getBlockCacheStats_nameConfigured_returnsBlockCacheStatsWithConfiguredName() {
+ ClockBlockCacheTable cacheTable = new ClockBlockCacheTable(
+ createBlockCacheConfig().setName(NAME));
+
+ assertThat(cacheTable.getBlockCacheStats(), hasSize(1));
+ assertThat(cacheTable.getBlockCacheStats().get(0).getName(),
+ equalTo(NAME));
+ }
+
+ @Test
+ public void getAllBlockCacheStats() {
+ ClockBlockCacheTable cacheTable = new ClockBlockCacheTable(
+ createBlockCacheConfig());
+
+ List<BlockCacheStats> blockCacheStats = cacheTable.getBlockCacheStats();
+ assertThat(blockCacheStats, contains(isA(BlockCacheStats.class)));
+ }
+
+ private static DfsBlockCacheConfig createBlockCacheConfig() {
+ return new DfsBlockCacheConfig().setBlockSize(512)
+ .setConcurrencyLevel(4).setBlockLimit(1024);
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfigTest.java
index 2df0ba1b05..afa3179cde 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfigTest.java
@@ -38,13 +38,37 @@
package org.eclipse.jgit.internal.storage.dfs;
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DEFAULT_NAME;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_CACHE_PREFIX;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_LIMIT;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_SIZE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONCURRENCY_LEVEL;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACK_EXTENSIONS;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_RATIO;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.closeTo;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThrows;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DfsBlockCachePackExtConfig;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.lib.Config;
import org.junit.Test;
+@SuppressWarnings("boxing")
public class DfsBlockCacheConfigTest {
@Test
@@ -55,7 +79,6 @@ public class DfsBlockCacheConfigTest {
}
@Test
- @SuppressWarnings("boxing")
public void negativeBlockSizeIsConvertedToDefault() {
DfsBlockCacheConfig config = new DfsBlockCacheConfig();
config.setBlockSize(-1);
@@ -64,7 +87,6 @@ public class DfsBlockCacheConfigTest {
}
@Test
- @SuppressWarnings("boxing")
public void tooSmallBlockSizeIsConvertedToDefault() {
DfsBlockCacheConfig config = new DfsBlockCacheConfig();
config.setBlockSize(10);
@@ -73,11 +95,295 @@ public class DfsBlockCacheConfigTest {
}
@Test
- @SuppressWarnings("boxing")
public void validBlockSize() {
DfsBlockCacheConfig config = new DfsBlockCacheConfig();
config.setBlockSize(65536);
assertThat(config.getBlockSize(), is(65536));
}
+
+ @Test
+ public void fromConfigs() {
+ Config config = new Config();
+ config.setLong(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_BLOCK_LIMIT, 50 * 1024);
+ config.setInt(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_BLOCK_SIZE, 1024);
+ config.setInt(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_CONCURRENCY_LEVEL, 3);
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_STREAM_RATIO, "0.5");
+
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig()
+ .fromConfig(config);
+ assertThat(cacheConfig.getBlockLimit(), is(50L * 1024L));
+ assertThat(cacheConfig.getBlockSize(), is(1024));
+ assertThat(cacheConfig.getConcurrencyLevel(), is(3));
+ assertThat(cacheConfig.getStreamRatio(), closeTo(0.5, 0.0001));
+ }
+
+ @Test
+ public void fromConfig_blockLimitNotAMultipleOfBlockSize_throws() {
+ Config config = new Config();
+ config.setLong(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_BLOCK_LIMIT, 1025);
+ config.setInt(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_BLOCK_SIZE, 1024);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new DfsBlockCacheConfig().fromConfig(config));
+ }
+
+ @Test
+ public void fromConfig_streamRatioInvalidFormat_throws() {
+ Config config = new Config();
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_STREAM_RATIO, "0.a5");
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new DfsBlockCacheConfig().fromConfig(config));
+ }
+
+ @Test
+ public void fromConfig_generatesDfsBlockCachePackExtConfigs() {
+ Config config = new Config();
+ addPackExtConfigEntry(config, "pack", List.of(PackExt.PACK),
+ /* blockLimit= */ 20 * 512, /* blockSize= */ 512);
+
+ addPackExtConfigEntry(config, "bitmap", List.of(PackExt.BITMAP_INDEX),
+ /* blockLimit= */ 25 * 1024, /* blockSize= */ 1024);
+
+ addPackExtConfigEntry(config, "index",
+ List.of(PackExt.INDEX, PackExt.OBJECT_SIZE_INDEX,
+ PackExt.REVERSE_INDEX),
+ /* blockLimit= */ 30 * 1024, /* blockSize= */ 1024);
+
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig()
+ .fromConfig(config);
+ var configs = cacheConfig.getPackExtCacheConfigurations();
+ assertThat(configs, hasSize(3));
+ var packConfig = getConfigForExt(configs, PackExt.PACK);
+ assertThat(packConfig.getBlockLimit(), is(20L * 512L));
+ assertThat(packConfig.getBlockSize(), is(512));
+
+ var bitmapConfig = getConfigForExt(configs, PackExt.BITMAP_INDEX);
+ assertThat(bitmapConfig.getBlockLimit(), is(25L * 1024L));
+ assertThat(bitmapConfig.getBlockSize(), is(1024));
+
+ var indexConfig = getConfigForExt(configs, PackExt.INDEX);
+ assertThat(indexConfig.getBlockLimit(), is(30L * 1024L));
+ assertThat(indexConfig.getBlockSize(), is(1024));
+ assertThat(getConfigForExt(configs, PackExt.OBJECT_SIZE_INDEX),
+ is(indexConfig));
+ assertThat(getConfigForExt(configs, PackExt.REVERSE_INDEX),
+ is(indexConfig));
+ }
+
+ @Test
+ public void fromConfig_withExistingCacheHotMap_configWithPackExtConfigsHasHotMaps() {
+ Config config = new Config();
+ addPackExtConfigEntry(config, "pack", List.of(PackExt.PACK),
+ /* blockLimit= */ 20 * 512, /* blockSize= */ 512);
+
+ addPackExtConfigEntry(config, "bitmap", List.of(PackExt.BITMAP_INDEX),
+ /* blockLimit= */ 25 * 1024, /* blockSize= */ 1024);
+
+ addPackExtConfigEntry(config, "index",
+ List.of(PackExt.INDEX, PackExt.OBJECT_SIZE_INDEX,
+ PackExt.REVERSE_INDEX),
+ /* blockLimit= */ 30 * 1024, /* blockSize= */ 1024);
+
+ Map<PackExt, Integer> cacheHotMap = Map.of(PackExt.PACK, 1,
+ PackExt.BITMAP_INDEX, 2, PackExt.INDEX, 3, PackExt.REFTABLE, 4);
+
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig();
+ cacheConfig.setCacheHotMap(cacheHotMap);
+ cacheConfig.fromConfig(config);
+
+ var configs = cacheConfig.getPackExtCacheConfigurations();
+ assertThat(cacheConfig.getCacheHotMap(), is(cacheHotMap));
+ assertThat(configs, hasSize(3));
+ var packConfig = getConfigForExt(configs, PackExt.PACK);
+ assertThat(packConfig.getCacheHotMap(), is(Map.of(PackExt.PACK, 1)));
+
+ var bitmapConfig = getConfigForExt(configs, PackExt.BITMAP_INDEX);
+ assertThat(bitmapConfig.getCacheHotMap(),
+ is(Map.of(PackExt.BITMAP_INDEX, 2)));
+
+ var indexConfig = getConfigForExt(configs, PackExt.INDEX);
+ assertThat(indexConfig.getCacheHotMap(), is(Map.of(PackExt.INDEX, 3)));
+ }
+
+ @Test
+ public void setCacheHotMap_configWithPackExtConfigs_setsHotMaps() {
+ Config config = new Config();
+ addPackExtConfigEntry(config, "pack", List.of(PackExt.PACK),
+ /* blockLimit= */ 20 * 512, /* blockSize= */ 512);
+
+ addPackExtConfigEntry(config, "bitmap", List.of(PackExt.BITMAP_INDEX),
+ /* blockLimit= */ 25 * 1024, /* blockSize= */ 1024);
+
+ addPackExtConfigEntry(config, "index",
+ List.of(PackExt.INDEX, PackExt.OBJECT_SIZE_INDEX,
+ PackExt.REVERSE_INDEX),
+ /* blockLimit= */ 30 * 1024, /* blockSize= */ 1024);
+
+ Map<PackExt, Integer> cacheHotMap = Map.of(PackExt.PACK, 1,
+ PackExt.BITMAP_INDEX, 2, PackExt.INDEX, 3, PackExt.REFTABLE, 4);
+
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig()
+ .fromConfig(config);
+ cacheConfig.setCacheHotMap(cacheHotMap);
+
+ var configs = cacheConfig.getPackExtCacheConfigurations();
+ assertThat(cacheConfig.getCacheHotMap(), is(cacheHotMap));
+ assertThat(configs, hasSize(3));
+ var packConfig = getConfigForExt(configs, PackExt.PACK);
+ assertThat(packConfig.getCacheHotMap(), is(Map.of(PackExt.PACK, 1)));
+
+ var bitmapConfig = getConfigForExt(configs, PackExt.BITMAP_INDEX);
+ assertThat(bitmapConfig.getCacheHotMap(),
+ is(Map.of(PackExt.BITMAP_INDEX, 2)));
+
+ var indexConfig = getConfigForExt(configs, PackExt.INDEX);
+ assertThat(indexConfig.getCacheHotMap(), is(Map.of(PackExt.INDEX, 3)));
+ }
+
+ @Test
+ public void fromConfigs_baseConfigOnly_nameSetFromConfigDfsSubSection() {
+ Config config = new Config();
+
+ DfsBlockCacheConfig blockCacheConfig = new DfsBlockCacheConfig()
+ .fromConfig(config);
+ assertThat(blockCacheConfig.getName(), equalTo(DEFAULT_NAME));
+ }
+
+ @Test
+ public void fromConfigs_namesSetFromConfigDfsCachePrefixSubSections() {
+ Config config = new Config();
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_STREAM_RATIO, "0.5");
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_CACHE_PREFIX + "name1",
+ CONFIG_KEY_PACK_EXTENSIONS, PackExt.PACK.name());
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_CACHE_PREFIX + "name2",
+ CONFIG_KEY_PACK_EXTENSIONS, PackExt.BITMAP_INDEX.name());
+
+ DfsBlockCacheConfig blockCacheConfig = new DfsBlockCacheConfig()
+ .fromConfig(config);
+ assertThat(blockCacheConfig.getName(), equalTo("dfs"));
+ assertThat(
+ blockCacheConfig.getPackExtCacheConfigurations().get(0)
+ .getPackExtCacheConfiguration().getName(),
+ equalTo("dfs.name1"));
+ assertThat(
+ blockCacheConfig.getPackExtCacheConfigurations().get(1)
+ .getPackExtCacheConfiguration().getName(),
+ equalTo("dfs.name2"));
+ }
+
+ @Test
+ public void fromConfigs_dfsBlockCachePackExtConfigWithDuplicateExtensions_throws() {
+ Config config = new Config();
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_CACHE_PREFIX + "pack1",
+ CONFIG_KEY_PACK_EXTENSIONS, PackExt.PACK.name());
+
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_CACHE_PREFIX + "pack2",
+ CONFIG_KEY_PACK_EXTENSIONS, PackExt.PACK.name());
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new DfsBlockCacheConfig().fromConfig(config));
+ }
+
+ @Test
+ public void fromConfigs_dfsBlockCachePackExtConfigWithEmptyExtensions_throws() {
+ Config config = new Config();
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_CACHE_PREFIX + "pack1",
+ CONFIG_KEY_PACK_EXTENSIONS, "");
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new DfsBlockCacheConfig().fromConfig(config));
+ }
+
+ @Test
+ public void fromConfigs_dfsBlockCachePackExtConfigWithNoExtensions_throws() {
+ Config config = new Config();
+ config.setInt(CONFIG_CORE_SECTION, CONFIG_DFS_CACHE_PREFIX + "pack1",
+ CONFIG_KEY_BLOCK_SIZE, 0);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new DfsBlockCacheConfig().fromConfig(config));
+ }
+
+ @Test
+ public void fromConfigs_dfsBlockCachePackExtConfigWithUnknownExtensions_throws() {
+ Config config = new Config();
+ config.setString(CONFIG_CORE_SECTION,
+ CONFIG_DFS_CACHE_PREFIX + "unknownExt",
+ CONFIG_KEY_PACK_EXTENSIONS, "NotAKnownExt");
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new DfsBlockCacheConfig().fromConfig(config));
+ }
+
+ @Test
+ public void writeConfigurationDebug_writesConfigsToWriter()
+ throws Exception {
+ Config config = new Config();
+ config.setLong(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_BLOCK_LIMIT, 50 * 1024);
+ config.setInt(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_BLOCK_SIZE, 1024);
+ config.setInt(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_CONCURRENCY_LEVEL, 3);
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_STREAM_RATIO, "0.5");
+ addPackExtConfigEntry(config, "pack", List.of(PackExt.PACK),
+ /* blockLimit= */ 20 * 512, /* blockSize= */ 512);
+
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig()
+ .fromConfig(config);
+ Map<PackExt, Integer> hotmap = Map.of(PackExt.PACK, 10);
+ cacheConfig.setCacheHotMap(hotmap);
+
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ cacheConfig.print(new PrintWriter(byteArrayOutputStream, true,
+ StandardCharsets.UTF_8));
+
+ String writenConfig = byteArrayOutputStream
+ .toString(StandardCharsets.UTF_8);
+
+ List<String> writenLines = Arrays.asList(writenConfig.split("\n"));
+ assertThat(writenLines,
+ equalTo(List.of("Name: dfs", " BlockLimit: " + (50 * 1024),
+ " BlockSize: 1024", " StreamRatio: 0.5",
+ " ConcurrencyLevel: 3",
+ " CacheHotMapEntry: " + PackExt.PACK + " : " + 10,
+ " Name: dfs.pack", " BlockLimit: " + 20 * 512,
+ " BlockSize: 512", " StreamRatio: 0.3",
+ " ConcurrencyLevel: 32",
+ " CacheHotMapEntry: " + PackExt.PACK + " : " + 10,
+ " PackExts: " + List.of(PackExt.PACK))));
+ }
+
+ private static void addPackExtConfigEntry(Config config, String configName,
+ List<PackExt> packExts, long blockLimit, int blockSize) {
+ String packExtConfigName = CONFIG_DFS_CACHE_PREFIX + configName;
+ config.setString(CONFIG_CORE_SECTION, packExtConfigName,
+ CONFIG_KEY_PACK_EXTENSIONS, packExts.stream().map(PackExt::name)
+ .collect(Collectors.joining(" ")));
+ config.setLong(CONFIG_CORE_SECTION, packExtConfigName,
+ CONFIG_KEY_BLOCK_LIMIT, blockLimit);
+ config.setInt(CONFIG_CORE_SECTION, packExtConfigName,
+ CONFIG_KEY_BLOCK_SIZE, blockSize);
+ }
+
+ private static DfsBlockCacheConfig getConfigForExt(
+ List<DfsBlockCachePackExtConfig> configs, PackExt packExt) {
+ for (DfsBlockCachePackExtConfig config : configs) {
+ if (config.getPackExts().contains(packExt)) {
+ return config.getPackExtCacheConfiguration();
+ }
+ }
+ return null;
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java
index fef0563f48..3c7cc075d2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java
@@ -13,20 +13,24 @@ package org.eclipse.jgit.internal.storage.dfs;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.LongStream;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.LongStream;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DfsBlockCachePackExtConfig;
import org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.IndexEventConsumer;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.junit.TestRepository;
@@ -39,14 +43,35 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
+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 DfsBlockCacheTest {
@Rule
public TestName testName = new TestName();
+
private TestRng rng;
+
private DfsBlockCache cache;
+
private ExecutorService pool;
+ private enum CacheType {
+ SINGLE_TABLE_CLOCK_BLOCK_CACHE, EXT_SPLIT_TABLE_CLOCK_BLOCK_CACHE
+ }
+
+ @Parameters(name = "cache type: {0}")
+ public static Iterable<? extends Object> data() {
+ return Arrays.asList(CacheType.SINGLE_TABLE_CLOCK_BLOCK_CACHE,
+ CacheType.EXT_SPLIT_TABLE_CLOCK_BLOCK_CACHE);
+ }
+
+ @Parameter
+ public CacheType cacheType;
+
@Before
public void setUp() {
rng = new TestRng(testName.getMethodName());
@@ -448,8 +473,28 @@ public class DfsBlockCacheTest {
}
private void resetCache(int concurrencyLevel) {
- DfsBlockCache.reconfigure(new DfsBlockCacheConfig().setBlockSize(512)
- .setConcurrencyLevel(concurrencyLevel).setBlockLimit(1 << 20));
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig()
+ .setBlockSize(512).setConcurrencyLevel(concurrencyLevel)
+ .setBlockLimit(1 << 20);
+ switch (cacheType) {
+ case SINGLE_TABLE_CLOCK_BLOCK_CACHE:
+ // SINGLE_TABLE_CLOCK_BLOCK_CACHE doesn't modify the config.
+ break;
+ case EXT_SPLIT_TABLE_CLOCK_BLOCK_CACHE:
+ List<DfsBlockCachePackExtConfig> packExtCacheConfigs = new ArrayList<>();
+ for (PackExt packExt : PackExt.values()) {
+ DfsBlockCacheConfig extCacheConfig = new DfsBlockCacheConfig()
+ .setBlockSize(512).setConcurrencyLevel(concurrencyLevel)
+ .setBlockLimit(1 << 20)
+ .setPackExtCacheConfigurations(packExtCacheConfigs);
+ packExtCacheConfigs.add(new DfsBlockCachePackExtConfig(
+ EnumSet.of(packExt), extCacheConfig));
+ }
+ cacheConfig.setPackExtCacheConfigurations(packExtCacheConfigs);
+ break;
+ }
+ assertNotNull(cacheConfig);
+ DfsBlockCache.reconfigure(cacheConfig);
cache = DfsBlockCache.getInstance();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
index e193de9764..00a3760e21 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
@@ -6,6 +6,7 @@ import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.IN
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -15,14 +16,18 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
+
import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
import org.eclipse.jgit.internal.storage.commitgraph.CommitGraphWriter;
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.reftable.LogCursor;
import org.eclipse.jgit.internal.storage.reftable.RefCursor;
import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
@@ -36,6 +41,7 @@ import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevBlob;
@@ -43,6 +49,7 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.util.GitTimeParser;
import org.eclipse.jgit.util.SystemReader;
import org.junit.After;
import org.junit.Before;
@@ -1171,6 +1178,7 @@ public class DfsGarbageCollectorTest {
gcWithObjectSizeIndex(10);
+ odb.getReaderOptions().setUseObjectSizeIndex(true);
DfsReader reader = odb.newReader();
DfsPackFile gcPack = findFirstBySource(odb.getPacks(), GC);
assertTrue(gcPack.hasObjectSizeIndex(reader));
@@ -1191,6 +1199,7 @@ public class DfsGarbageCollectorTest {
gcWithObjectSizeIndex(10);
+ odb.getReaderOptions().setUseObjectSizeIndex(true);
DfsReader reader = odb.newReader();
DfsPackFile gcPack = findFirstBySource(odb.getPacks(), GC);
assertTrue(gcPack.hasObjectSizeIndex(reader));
@@ -1272,6 +1281,87 @@ public class DfsGarbageCollectorTest {
bitmapIndex.getXorBitmapCount() > 0);
}
+ @Test
+ public void gitGCWithRefLogExpire() throws Exception {
+ String master = "refs/heads/master";
+ RevCommit commit0 = commit().message("0").create();
+ RevCommit commit1 = commit().message("1").parent(commit0).create();
+ git.update(master, commit1);
+ DfsGarbageCollector gc = new DfsGarbageCollector(repo);
+ gc.setReftableConfig(new ReftableConfig());
+ run(gc);
+ DfsPackDescription t1 = odb.newPack(INSERT);
+ Ref next = new ObjectIdRef.PeeledNonTag(Ref.Storage.LOOSE,
+ "refs/heads/next", commit0.copy());
+ Instant currentDay = Instant.now();
+ Instant ten_days_ago = GitTimeParser.parseInstant("10 days ago");
+ Instant twenty_days_ago = GitTimeParser.parseInstant("20 days ago");
+ Instant thirty_days_ago = GitTimeParser.parseInstant("30 days ago");
+ Instant fifty_days_ago = GitTimeParser.parseInstant("50 days ago");
+ final ZoneOffset offset = ZoneOffset.ofHours(-8);
+ PersonIdent who2 = new PersonIdent("J.Author", "authemail", currentDay,
+ offset);
+ PersonIdent who3 = new PersonIdent("J.Author", "authemail",
+ ten_days_ago, offset);
+ PersonIdent who4 = new PersonIdent("J.Author", "authemail",
+ twenty_days_ago, offset);
+ PersonIdent who5 = new PersonIdent("J.Author", "authemail",
+ thirty_days_ago, offset);
+ PersonIdent who6 = new PersonIdent("J.Author", "authemail",
+ fifty_days_ago, offset);
+
+ try (DfsOutputStream out = odb.writeFile(t1, REFTABLE)) {
+ ReftableWriter w = new ReftableWriter(out);
+ w.setMinUpdateIndex(42);
+ w.setMaxUpdateIndex(42);
+ w.begin();
+ w.sortAndWriteRefs(Collections.singleton(next));
+ w.writeLog("refs/heads/branch", 1, who2, ObjectId.zeroId(),id(2), "Branch Message");
+ w.writeLog("refs/heads/branch1", 2, who3, ObjectId.zeroId(),id(3), "Branch Message1");
+ w.writeLog("refs/heads/branch2", 2, who4, ObjectId.zeroId(),id(4), "Branch Message2");
+ w.writeLog("refs/heads/branch3", 2, who5, ObjectId.zeroId(),id(5), "Branch Message3");
+ w.writeLog("refs/heads/branch4", 2, who6, ObjectId.zeroId(),id(6), "Branch Message4");
+ w.finish();
+ t1.addFileExt(REFTABLE);
+ t1.setReftableStats(w.getStats());
+ }
+ odb.commitPack(Collections.singleton(t1), null);
+
+ gc = new DfsGarbageCollector(repo);
+ gc.setReftableConfig(new ReftableConfig());
+ // Expire ref log entries older than 30 days
+ gc.setRefLogExpire(thirty_days_ago);
+ run(gc);
+
+ // Single GC pack present with all objects.
+ assertEquals(1, odb.getPacks().length);
+ DfsPackFile pack = odb.getPacks()[0];
+ DfsPackDescription desc = pack.getPackDescription();
+
+ DfsReftable table = new DfsReftable(DfsBlockCache.getInstance(), desc);
+ try (DfsReader ctx = odb.newReader();
+ ReftableReader rr = table.open(ctx);
+ RefCursor rc = rr.allRefs();
+ LogCursor lc = rr.allLogs()) {
+ assertTrue(rc.next());
+ assertEquals(master, rc.getRef().getName());
+ assertEquals(commit1, rc.getRef().getObjectId());
+ assertTrue(rc.next());
+ assertEquals(next.getName(), rc.getRef().getName());
+ assertEquals(commit0, rc.getRef().getObjectId());
+ assertFalse(rc.next());
+ assertTrue(lc.next());
+ assertEquals(lc.getRefName(),"refs/heads/branch");
+ assertTrue(lc.next());
+ assertEquals(lc.getRefName(),"refs/heads/branch1");
+ assertTrue(lc.next());
+ assertEquals(lc.getRefName(),"refs/heads/branch2");
+ // Old entries are purged
+ assertFalse(lc.next());
+ }
+ }
+
+
private RevCommit commitChain(RevCommit parent, int length)
throws Exception {
for (int i = 0; i < length; i++) {
@@ -1361,4 +1451,12 @@ public class DfsGarbageCollectorTest {
}
return cnt;
}
+ private static ObjectId id(int i) {
+ byte[] buf = new byte[OBJECT_ID_LENGTH];
+ buf[0] = (byte) (i & 0xff);
+ buf[1] = (byte) ((i >>> 8) & 0xff);
+ buf[2] = (byte) ((i >>> 16) & 0xff);
+ buf[3] = (byte) (i >>> 24);
+ return ObjectId.fromRaw(buf);
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
index b84a0b00ae..0b558edf2c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
@@ -295,6 +295,7 @@ public class DfsInserterTest {
public void testObjectSizeIndexOnInsert() throws IOException {
db.getConfig().setInt(CONFIG_PACK_SECTION, null,
CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX, 0);
+ db.getObjectDatabase().getReaderOptions().setUseObjectSizeIndex(true);
byte[] contents = Constants.encode("foo");
ObjectId fooId;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java
index c516e30f50..c3b6aa85a2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java
@@ -12,13 +12,18 @@ 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.INSERT;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.OBJECT_SIZE_INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.Optional;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Test;
@@ -98,6 +103,40 @@ public class DfsPackCompacterTest {
pack.getPackDescription().getEstimatedPackSize());
}
+ @Test
+ public void testObjectSizeIndexWritten() throws Exception {
+ writeObjectSizeIndex(repo, true);
+ RevCommit commit0 = commit().message("0").create();
+ RevCommit commit1 = commit().message("1").parent(commit0).create();
+ git.update("master", commit1);
+
+ compact();
+
+ Optional<DfsPackFile> compactPack = Arrays.stream(odb.getPacks())
+ .filter(pack -> pack.getPackDescription()
+ .getPackSource() == COMPACT)
+ .findFirst();
+ assertTrue(compactPack.isPresent());
+ assertTrue(compactPack.get().getPackDescription().hasFileExt(OBJECT_SIZE_INDEX));
+ }
+
+ @Test
+ public void testObjectSizeIndexNotWritten() throws Exception {
+ writeObjectSizeIndex(repo, false);
+ RevCommit commit0 = commit().message("0").create();
+ RevCommit commit1 = commit().message("1").parent(commit0).create();
+ git.update("master", commit1);
+
+ compact();
+
+ Optional<DfsPackFile> compactPack = Arrays.stream(odb.getPacks())
+ .filter(pack -> pack.getPackDescription()
+ .getPackSource() == COMPACT)
+ .findFirst();
+ assertTrue(compactPack.isPresent());
+ assertFalse(compactPack.get().getPackDescription().hasFileExt(OBJECT_SIZE_INDEX));
+ }
+
private TestRepository<InMemoryRepository>.CommitBuilder commit() {
return git.commit();
}
@@ -108,4 +147,9 @@ public class DfsPackCompacterTest {
compactor.compact(null);
odb.clearCache();
}
+
+ private static void writeObjectSizeIndex(DfsRepository repo, boolean should) {
+ repo.getConfig().setInt(ConfigConstants.CONFIG_PACK_SECTION, null,
+ ConfigConstants.CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX, should ? 0 : -1);
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java
index d21e51f276..9680019f88 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java
@@ -41,6 +41,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.junit.Before;
import org.junit.Test;
@@ -126,6 +127,7 @@ public class DfsPackFileTest {
setObjectSizeIndexMinBytes(0);
ObjectId blobId = setupPack(512, 800);
+ db.getObjectDatabase().getReaderOptions().setUseObjectSizeIndex(true);
DfsReader reader = db.getObjectDatabase().newReader();
DfsPackFile pack = db.getObjectDatabase().getPacks()[0];
assertTrue(pack.hasObjectSizeIndex(reader));
@@ -308,7 +310,7 @@ public class DfsPackFileTest {
private void assertPackSize() throws IOException {
try (DfsReader ctx = db.getObjectDatabase().newReader();
- PackWriter pw = new PackWriter(ctx);
+ PackWriter pw = new PackWriter(new PackConfig(), ctx);
ByteArrayOutputStream os = new ByteArrayOutputStream();
PackOutputStream out = new PackOutputStream(
NullProgressMonitor.INSTANCE, os, pw)) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java
index 130af27773..c1cd231c66 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java
@@ -61,6 +61,7 @@ public class DfsPackParserTest {
ins.flush();
}
+ repo.getObjectDatabase().getReaderOptions().setUseObjectSizeIndex(true);
DfsReader reader = repo.getObjectDatabase().newReader();
PackList packList = repo.getObjectDatabase().getPackList();
assertEquals(1, packList.packs.length);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java
index 254184ee80..a0c228906e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java
@@ -37,6 +37,8 @@ public class DfsReaderTest {
@Before
public void setUp() {
db = new InMemoryRepository(new DfsRepositoryDescription("test"));
+ // These tests assume the object size index is enabled.
+ db.getObjectDatabase().getReaderOptions().setUseObjectSizeIndex(true);
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTableTest.java
new file mode 100644
index 0000000000..e7627bc4ab
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTableTest.java
@@ -0,0 +1,679 @@
+/*
+ * Copyright (c) 2024, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheTable.BlockCacheStats;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.Ref;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.RefLoader;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DfsBlockCachePackExtConfig;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+@SuppressWarnings({ "boxing", "unchecked" })
+public class PackExtBlockCacheTableTest {
+ private static final String CACHE_NAME = "CacheName";
+
+ @Test
+ public void fromBlockCacheConfigs_createsDfsPackExtBlockCacheTables() {
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig();
+ cacheConfig.setPackExtCacheConfigurations(
+ List.of(new DfsBlockCachePackExtConfig(EnumSet.of(PackExt.PACK),
+ new DfsBlockCacheConfig())));
+ assertNotNull(
+ PackExtBlockCacheTable.fromBlockCacheConfigs(cacheConfig));
+ }
+
+ @Test
+ public void fromBlockCacheConfigs_noPackExtConfigurationGiven_packExtCacheConfigurationsIsEmpty_throws() {
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig();
+ cacheConfig.setPackExtCacheConfigurations(List.of());
+ assertThrows(IllegalArgumentException.class,
+ () -> PackExtBlockCacheTable
+ .fromBlockCacheConfigs(cacheConfig));
+ }
+
+ @Test
+ public void hasBlock0_packExtMapsToCacheTable_callsBitmapIndexCacheTable() {
+ DfsStreamKey streamKey = new TestKey(PackExt.BITMAP_INDEX);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.hasBlock0(any(DfsStreamKey.class)))
+ .thenReturn(true);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertTrue(tables.hasBlock0(streamKey));
+ }
+
+ @Test
+ public void hasBlock0_packExtDoesNotMapToCacheTable_callsDefaultCache() {
+ DfsStreamKey streamKey = new TestKey(PackExt.PACK);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.hasBlock0(any(DfsStreamKey.class)))
+ .thenReturn(true);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertTrue(tables.hasBlock0(streamKey));
+ }
+
+ @Test
+ public void getOrLoad_packExtMapsToCacheTable_callsBitmapIndexCacheTable()
+ throws Exception {
+ BlockBasedFile blockBasedFile = new BlockBasedFile(null,
+ mock(DfsPackDescription.class), PackExt.BITMAP_INDEX) {
+ // empty
+ };
+ DfsBlock dfsBlock = mock(DfsBlock.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.getOrLoad(any(BlockBasedFile.class),
+ anyLong(), any(DfsReader.class),
+ any(DfsBlockCache.ReadableChannelSupplier.class)))
+ .thenReturn(mock(DfsBlock.class));
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.getOrLoad(any(BlockBasedFile.class),
+ anyLong(), any(DfsReader.class),
+ any(DfsBlockCache.ReadableChannelSupplier.class)))
+ .thenReturn(dfsBlock);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(
+ tables.getOrLoad(blockBasedFile, 0, mock(DfsReader.class),
+ mock(DfsBlockCache.ReadableChannelSupplier.class)),
+ sameInstance(dfsBlock));
+ }
+
+ @Test
+ public void getOrLoad_packExtDoesNotMapToCacheTable_callsDefaultCache()
+ throws Exception {
+ BlockBasedFile blockBasedFile = new BlockBasedFile(null,
+ mock(DfsPackDescription.class), PackExt.PACK) {
+ // empty
+ };
+ DfsBlock dfsBlock = mock(DfsBlock.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.getOrLoad(any(BlockBasedFile.class),
+ anyLong(), any(DfsReader.class),
+ any(DfsBlockCache.ReadableChannelSupplier.class)))
+ .thenReturn(dfsBlock);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.getOrLoad(any(BlockBasedFile.class),
+ anyLong(), any(DfsReader.class),
+ any(DfsBlockCache.ReadableChannelSupplier.class)))
+ .thenReturn(mock(DfsBlock.class));
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(
+ tables.getOrLoad(blockBasedFile, 0, mock(DfsReader.class),
+ mock(DfsBlockCache.ReadableChannelSupplier.class)),
+ sameInstance(dfsBlock));
+ }
+
+ @Test
+ public void getOrLoadRef_packExtMapsToCacheTable_callsBitmapIndexCacheTable()
+ throws Exception {
+ Ref<Integer> ref = mock(Ref.class);
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.BITMAP_INDEX);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.getOrLoadRef(any(DfsStreamKey.class),
+ anyLong(), any(RefLoader.class))).thenReturn(mock(Ref.class));
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.getOrLoadRef(any(DfsStreamKey.class),
+ anyLong(), any(RefLoader.class))).thenReturn(ref);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.getOrLoadRef(dfsStreamKey, 0, mock(RefLoader.class)),
+ sameInstance(ref));
+ }
+
+ @Test
+ public void getOrLoadRef_packExtDoesNotMapToCacheTable_callsDefaultCache()
+ throws Exception {
+ Ref<Integer> ref = mock(Ref.class);
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.PACK);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.getOrLoadRef(any(DfsStreamKey.class),
+ anyLong(), any(RefLoader.class))).thenReturn(ref);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.getOrLoadRef(any(DfsStreamKey.class),
+ anyLong(), any(RefLoader.class))).thenReturn(mock(Ref.class));
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.getOrLoadRef(dfsStreamKey, 0, mock(RefLoader.class)),
+ sameInstance(ref));
+ }
+
+ @Test
+ public void putDfsBlock_packExtMapsToCacheTable_callsBitmapIndexCacheTable() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.BITMAP_INDEX);
+ DfsBlock dfsBlock = new DfsBlock(dfsStreamKey, 0, new byte[0]);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ tables.put(dfsBlock);
+ Mockito.verify(bitmapIndexCacheTable, times(1)).put(dfsBlock);
+ }
+
+ @Test
+ public void putDfsBlock_packExtDoesNotMapToCacheTable_callsDefaultCache() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.PACK);
+ DfsBlock dfsBlock = new DfsBlock(dfsStreamKey, 0, new byte[0]);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ tables.put(dfsBlock);
+ Mockito.verify(defaultBlockCacheTable, times(1)).put(dfsBlock);
+ }
+
+ @Test
+ public void putDfsStreamKey_packExtMapsToCacheTable_callsBitmapIndexCacheTable() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.BITMAP_INDEX);
+ Ref<Integer> ref = mock(Ref.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.put(any(DfsStreamKey.class), anyLong(),
+ anyLong(), anyInt())).thenReturn(mock(Ref.class));
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.put(any(DfsStreamKey.class), anyLong(),
+ anyLong(), anyInt())).thenReturn(ref);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.put(dfsStreamKey, 0, 0, 0), sameInstance(ref));
+ }
+
+ @Test
+ public void putDfsStreamKey_packExtDoesNotMapToCacheTable_callsDefaultCache() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.PACK);
+ Ref<Integer> ref = mock(Ref.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.put(any(DfsStreamKey.class), anyLong(),
+ anyLong(), anyInt())).thenReturn(ref);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.put(any(DfsStreamKey.class), anyLong(),
+ anyLong(), anyInt())).thenReturn(mock(Ref.class));
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.put(dfsStreamKey, 0, 0, 0), sameInstance(ref));
+ }
+
+ @Test
+ public void putRef_packExtMapsToCacheTable_callsBitmapIndexCacheTable() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.BITMAP_INDEX);
+ Ref<Integer> ref = mock(Ref.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.putRef(any(DfsStreamKey.class), anyLong(),
+ anyInt())).thenReturn(mock(Ref.class));
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.putRef(any(DfsStreamKey.class), anyLong(),
+ anyInt())).thenReturn(ref);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.putRef(dfsStreamKey, 0, 0), sameInstance(ref));
+ }
+
+ @Test
+ public void putRef_packExtDoesNotMapToCacheTable_callsDefaultCache() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.PACK);
+ Ref<Integer> ref = mock(Ref.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.putRef(any(DfsStreamKey.class), anyLong(),
+ anyInt())).thenReturn(ref);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.putRef(any(DfsStreamKey.class), anyLong(),
+ anyInt())).thenReturn(mock(Ref.class));
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.putRef(dfsStreamKey, 0, 0), sameInstance(ref));
+ }
+
+ @Test
+ public void contains_packExtMapsToCacheTable_callsBitmapIndexCacheTable() {
+ DfsStreamKey streamKey = new TestKey(PackExt.BITMAP_INDEX);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.contains(any(DfsStreamKey.class), anyLong()))
+ .thenReturn(true);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertTrue(tables.contains(streamKey, 0));
+ }
+
+ @Test
+ public void contains_packExtDoesNotMapToCacheTable_callsDefaultCache() {
+ DfsStreamKey streamKey = new TestKey(PackExt.PACK);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.contains(any(DfsStreamKey.class),
+ anyLong())).thenReturn(true);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertTrue(tables.contains(streamKey, 0));
+ }
+
+ @Test
+ public void get_packExtMapsToCacheTable_callsBitmapIndexCacheTable() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.BITMAP_INDEX);
+ Ref<Integer> ref = mock(Ref.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.get(any(DfsStreamKey.class), anyLong()))
+ .thenReturn(mock(Ref.class));
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.get(any(DfsStreamKey.class), anyLong()))
+ .thenReturn(ref);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.get(dfsStreamKey, 0), sameInstance(ref));
+ }
+
+ @Test
+ public void get_packExtDoesNotMapToCacheTable_callsDefaultCache() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.PACK);
+ Ref<Integer> ref = mock(Ref.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.get(any(DfsStreamKey.class), anyLong()))
+ .thenReturn(ref);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.get(any(DfsStreamKey.class), anyLong()))
+ .thenReturn(mock(Ref.class));
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.get(dfsStreamKey, 0), sameInstance(ref));
+ }
+
+ @Test
+ public void getName() {
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ cacheTableWithStats(/* name= */ "defaultName", packStats),
+ Map.of(PackExt.PACK, cacheTableWithStats(/* name= */ "packName",
+ packStats)));
+
+ assertThat(tables.getName(), equalTo("defaultName,packName"));
+ }
+
+ @Test
+ public void getAllBlockCacheStats() {
+ String defaultTableName = "default table";
+ DfsBlockCacheStats defaultStats = new DfsBlockCacheStats(
+ defaultTableName);
+ incrementCounter(4,
+ () -> defaultStats.incrementHit(new TestKey(PackExt.REFTABLE)));
+
+ String packTableName = "pack table";
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats(packTableName);
+ incrementCounter(5,
+ () -> packStats.incrementHit(new TestKey(PackExt.PACK)));
+
+ String bitmapTableName = "bitmap table";
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats(
+ bitmapTableName);
+ incrementCounter(6, () -> bitmapStats
+ .incrementHit(new TestKey(PackExt.BITMAP_INDEX)));
+
+ DfsBlockCacheTable defaultTable = cacheTableWithStats(defaultStats);
+ DfsBlockCacheTable packTable = cacheTableWithStats(packStats);
+ DfsBlockCacheTable bitmapTable = cacheTableWithStats(bitmapStats);
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(defaultTable, Map.of(PackExt.PACK, packTable,
+ PackExt.BITMAP_INDEX, bitmapTable));
+
+ List<BlockCacheStats> statsList = tables.getBlockCacheStats();
+ assertThat(statsList, hasSize(3));
+
+ long[] defaultTableHitCounts = createEmptyStatsArray();
+ defaultTableHitCounts[PackExt.REFTABLE.getPosition()] = 4;
+ assertArrayEquals(
+ getCacheStatsByName(statsList, defaultTableName).getHitCount(),
+ defaultTableHitCounts);
+
+ long[] packTableHitCounts = createEmptyStatsArray();
+ packTableHitCounts[PackExt.PACK.getPosition()] = 5;
+ assertArrayEquals(
+ getCacheStatsByName(statsList, packTableName).getHitCount(),
+ packTableHitCounts);
+
+ long[] bitmapHitCounts = createEmptyStatsArray();
+ bitmapHitCounts[PackExt.BITMAP_INDEX.getPosition()] = 6;
+ assertArrayEquals(
+ getCacheStatsByName(statsList, bitmapTableName).getHitCount(),
+ bitmapHitCounts);
+ }
+
+ @Test
+ public void getBlockCacheStats_getCurrentSize_consolidatesAllTableCurrentSizes() {
+ long[] currentSizes = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ packStats.addToLiveBytes(new TestKey(PackExt.PACK), 5);
+ currentSizes[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ bitmapStats.addToLiveBytes(new TestKey(PackExt.BITMAP_INDEX), 6);
+ currentSizes[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ indexStats.addToLiveBytes(new TestKey(PackExt.INDEX), 7);
+ currentSizes[PackExt.INDEX.getPosition()] = 7;
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(cacheTableWithStats(packStats),
+ Map.of(PackExt.BITMAP_INDEX,
+ cacheTableWithStats(bitmapStats), PackExt.INDEX,
+ cacheTableWithStats(indexStats)));
+
+ assertArrayEquals(AggregatedBlockCacheStats
+ .fromStatsList(tables.getBlockCacheStats()).getCurrentSize(),
+ currentSizes);
+ }
+
+ @Test
+ public void getBlockCacheStats_GetHitCount_consolidatesAllTableHitCounts() {
+ long[] hitCounts = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementHit(new TestKey(PackExt.PACK)));
+ hitCounts[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> bitmapStats
+ .incrementHit(new TestKey(PackExt.BITMAP_INDEX)));
+ hitCounts[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementHit(new TestKey(PackExt.INDEX)));
+ hitCounts[PackExt.INDEX.getPosition()] = 7;
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(cacheTableWithStats(packStats),
+ Map.of(PackExt.BITMAP_INDEX,
+ cacheTableWithStats(bitmapStats), PackExt.INDEX,
+ cacheTableWithStats(indexStats)));
+
+ assertArrayEquals(AggregatedBlockCacheStats
+ .fromStatsList(tables.getBlockCacheStats()).getHitCount(),
+ hitCounts);
+ }
+
+ @Test
+ public void getBlockCacheStats_getMissCount_consolidatesAllTableMissCounts() {
+ long[] missCounts = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementMiss(new TestKey(PackExt.PACK)));
+ missCounts[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> bitmapStats
+ .incrementMiss(new TestKey(PackExt.BITMAP_INDEX)));
+ missCounts[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementMiss(new TestKey(PackExt.INDEX)));
+ missCounts[PackExt.INDEX.getPosition()] = 7;
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(cacheTableWithStats(packStats),
+ Map.of(PackExt.BITMAP_INDEX,
+ cacheTableWithStats(bitmapStats), PackExt.INDEX,
+ cacheTableWithStats(indexStats)));
+
+ assertArrayEquals(AggregatedBlockCacheStats
+ .fromStatsList(tables.getBlockCacheStats()).getMissCount(),
+ missCounts);
+ }
+
+ @Test
+ public void getBlockCacheStats_getTotalRequestCount_consolidatesAllTableTotalRequestCounts() {
+ long[] totalRequestCounts = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5, () -> {
+ packStats.incrementHit(new TestKey(PackExt.PACK));
+ packStats.incrementMiss(new TestKey(PackExt.PACK));
+ });
+ totalRequestCounts[PackExt.PACK.getPosition()] = 10;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> {
+ bitmapStats.incrementHit(new TestKey(PackExt.BITMAP_INDEX));
+ bitmapStats.incrementMiss(new TestKey(PackExt.BITMAP_INDEX));
+ });
+ totalRequestCounts[PackExt.BITMAP_INDEX.getPosition()] = 12;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7, () -> {
+ indexStats.incrementHit(new TestKey(PackExt.INDEX));
+ indexStats.incrementMiss(new TestKey(PackExt.INDEX));
+ });
+ totalRequestCounts[PackExt.INDEX.getPosition()] = 14;
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(cacheTableWithStats(packStats),
+ Map.of(PackExt.BITMAP_INDEX,
+ cacheTableWithStats(bitmapStats), PackExt.INDEX,
+ cacheTableWithStats(indexStats)));
+
+ assertArrayEquals(AggregatedBlockCacheStats
+ .fromStatsList(tables.getBlockCacheStats())
+ .getTotalRequestCount(), totalRequestCounts);
+ }
+
+ @Test
+ public void getBlockCacheStats_getHitRatio_consolidatesAllTableHitRatios() {
+ long[] hitRatios = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementHit(new TestKey(PackExt.PACK)));
+ hitRatios[PackExt.PACK.getPosition()] = 100;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> {
+ bitmapStats.incrementHit(new TestKey(PackExt.BITMAP_INDEX));
+ bitmapStats.incrementMiss(new TestKey(PackExt.BITMAP_INDEX));
+ });
+ hitRatios[PackExt.BITMAP_INDEX.getPosition()] = 50;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementMiss(new TestKey(PackExt.INDEX)));
+ hitRatios[PackExt.INDEX.getPosition()] = 0;
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(cacheTableWithStats(packStats),
+ Map.of(PackExt.BITMAP_INDEX,
+ cacheTableWithStats(bitmapStats), PackExt.INDEX,
+ cacheTableWithStats(indexStats)));
+
+ assertArrayEquals(AggregatedBlockCacheStats
+ .fromStatsList(tables.getBlockCacheStats()).getHitRatio(),
+ hitRatios);
+ }
+
+ @Test
+ public void getBlockCacheStats_getEvictions_consolidatesAllTableEvictions() {
+ long[] evictions = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementEvict(new TestKey(PackExt.PACK)));
+ evictions[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> bitmapStats
+ .incrementEvict(new TestKey(PackExt.BITMAP_INDEX)));
+ evictions[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementEvict(new TestKey(PackExt.INDEX)));
+ evictions[PackExt.INDEX.getPosition()] = 7;
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(cacheTableWithStats(packStats),
+ Map.of(PackExt.BITMAP_INDEX,
+ cacheTableWithStats(bitmapStats), PackExt.INDEX,
+ cacheTableWithStats(indexStats)));
+
+ assertArrayEquals(AggregatedBlockCacheStats
+ .fromStatsList(tables.getBlockCacheStats()).getEvictions(),
+ evictions);
+ }
+
+ private BlockCacheStats getCacheStatsByName(
+ List<BlockCacheStats> blockCacheStats, String name) {
+ for (BlockCacheStats entry : blockCacheStats) {
+ if (entry.getName().equals(name)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ private static void incrementCounter(int amount, Runnable fn) {
+ for (int i = 0; i < amount; i++) {
+ fn.run();
+ }
+ }
+
+ private static long[] createEmptyStatsArray() {
+ return new long[PackExt.values().length];
+ }
+
+ private static DfsBlockCacheTable cacheTableWithStats(
+ BlockCacheStats dfsBlockCacheStats) {
+ return cacheTableWithStats(CACHE_NAME, dfsBlockCacheStats);
+ }
+
+ private static DfsBlockCacheTable cacheTableWithStats(String name,
+ BlockCacheStats dfsBlockCacheStats) {
+ DfsBlockCacheTable cacheTable = mock(DfsBlockCacheTable.class);
+ when(cacheTable.getName()).thenReturn(name);
+ when(cacheTable.getBlockCacheStats())
+ .thenReturn(List.of(dfsBlockCacheStats));
+ return cacheTable;
+ }
+
+ private static class TestKey extends DfsStreamKey {
+ TestKey(PackExt packExt) {
+ super(0, packExt);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return false;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
index bd36337f35..41a33df0e4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
@@ -29,6 +29,7 @@ import java.util.List;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackIndexWriter;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
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/BasePackWriterTest.java
index 24a81b6715..92d7465376 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/BasePackWriterTest.java
@@ -66,7 +66,7 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
-public class PackWriterTest extends SampleDataRepositoryTestCase {
+public class BasePackWriterTest extends SampleDataRepositoryTestCase {
private static final List<RevObject> EMPTY_LIST_REVS = Collections
.<RevObject> emptyList();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java
index daf4382719..a0afc3ef13 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java
@@ -171,7 +171,7 @@ public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase {
assertEquals(c2.getResult(), ReceiveCommand.Result.OK);
}
- File packed = new File(diskRepo.getDirectory(), "packed-refs");
+ File packed = new File(diskRepo.getCommonDirectory(), "packed-refs");
String packedStr = new String(Files.readAllBytes(packed.toPath()),
UTF_8);
@@ -1263,7 +1263,7 @@ public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase {
}
private ReflogEntry getLastReflog(String name) throws IOException {
- ReflogReader r = diskRepo.getReflogReader(name);
+ ReflogReader r = diskRepo.getRefDatabase().getReflogReader(name);
if (r == null) {
return null;
}
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 32342e3563..5756b41442 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
@@ -23,6 +23,7 @@ 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 static org.junit.Assume.assumeTrue;
import java.io.File;
import java.io.FileOutputStream;
@@ -33,8 +34,15 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
-
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -51,6 +59,10 @@ import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.ExecutionResult;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.TemporaryBuffer;
import org.junit.Test;
public class FileReftableTest extends SampleDataRepositoryTestCase {
@@ -66,6 +78,30 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
@SuppressWarnings("boxing")
@Test
+ public void testReloadIfNecessary() throws Exception {
+ ObjectId id = db.resolve("master");
+ try (FileRepository repo1 = new FileRepository(db.getDirectory());
+ FileRepository repo2 = new FileRepository(db.getDirectory())) {
+ ((FileReftableDatabase) repo1.getRefDatabase())
+ .setAutoRefresh(true);
+ ((FileReftableDatabase) repo2.getRefDatabase())
+ .setAutoRefresh(true);
+ FileRepository repos[] = { repo1, repo2 };
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 2; j++) {
+ FileRepository repo = repos[j];
+ RefUpdate u = repo.getRefDatabase().newUpdate(
+ String.format("branch%d", i * 10 + j), false);
+ u.setNewObjectId(id);
+ RefUpdate.Result r = u.update();
+ assertEquals(Result.NEW, r);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("boxing")
+ @Test
public void testRacyReload() throws Exception {
ObjectId id = db.resolve("master");
int retry = 0;
@@ -87,13 +123,61 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
u.setNewObjectId(id);
r = u.update();
- assertEquals(r, Result.NEW);
+ assertEquals(Result.NEW, r);
}
}
}
// only the first one succeeds
- assertEquals(retry, 19);
+ assertEquals(19, retry);
+ }
+ }
+
+ @Test
+ public void testConcurrentRacyReload() throws Exception {
+ ObjectId id = db.resolve("master");
+ final CyclicBarrier barrier = new CyclicBarrier(2);
+
+ class UpdateRef implements Callable<RefUpdate.Result> {
+
+ private RefUpdate u;
+
+ UpdateRef(FileRepository repo, String branchName)
+ throws IOException {
+ u = repo.getRefDatabase().newUpdate(branchName,
+ false);
+ u.setNewObjectId(id);
+ }
+
+ @Override
+ public RefUpdate.Result call() throws Exception {
+ barrier.await(); // wait for the other thread to prepare
+ return u.update();
+ }
+ }
+
+ ExecutorService pool = Executors.newFixedThreadPool(2);
+ try (FileRepository repo1 = new FileRepository(db.getDirectory());
+ FileRepository repo2 = new FileRepository(db.getDirectory())) {
+ ((FileReftableDatabase) repo1.getRefDatabase())
+ .setAutoRefresh(true);
+ ((FileReftableDatabase) repo2.getRefDatabase())
+ .setAutoRefresh(true);
+ for (int i = 0; i < 10; i++) {
+ String branchName = String.format("branch%d",
+ Integer.valueOf(i));
+ Future<RefUpdate.Result> ru1 = pool
+ .submit(new UpdateRef(repo1, branchName));
+ Future<RefUpdate.Result> ru2 = pool
+ .submit(new UpdateRef(repo2, branchName));
+ assertTrue((ru1.get() == Result.NEW
+ && ru2.get() == Result.LOCK_FAILURE)
+ || (ru1.get() == Result.LOCK_FAILURE
+ && ru2.get() == Result.NEW));
+ }
+ } finally {
+ pool.shutdown();
+ pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}
}
@@ -105,13 +189,13 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
RefUpdate u = db.updateRef("refs/heads/master");
u.setForceUpdate(true);
u.setNewObjectId((i%2) == 0 ? c1 : c2);
- assertEquals(u.update(), FORCED);
+ assertEquals(FORCED, u.update());
}
File tableDir = new File(db.getDirectory(), Constants.REFTABLE);
assertTrue(tableDir.listFiles().length > 2);
((FileReftableDatabase)db.getRefDatabase()).compactFully();
- assertEquals(tableDir.listFiles().length,2);
+ assertEquals(2, tableDir.listFiles().length);
}
@Test
@@ -171,9 +255,10 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
v.update();
db.convertToPackedRefs(true, false);
- List<ReflogEntry> logs = db.getReflogReader("refs/heads/master").getReverseEntries(2);
- assertEquals(logs.get(0).getComment(), "banana");
- assertEquals(logs.get(1).getComment(), "apple");
+ List<ReflogEntry> logs = db.getRefDatabase()
+ .getReflogReader("refs/heads/master").getReverseEntries(2);
+ assertEquals("banana", logs.get(0).getComment());
+ assertEquals("apple", logs.get(1).getComment());
}
@Test
@@ -185,8 +270,9 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
ReceiveCommand rc1 = new ReceiveCommand(ObjectId.zeroId(), cur, "refs/heads/batch1");
ReceiveCommand rc2 = new ReceiveCommand(ObjectId.zeroId(), prev, "refs/heads/batch2");
String msg = "message";
+ RefDatabase refDb = db.getRefDatabase();
try (RevWalk rw = new RevWalk(db)) {
- db.getRefDatabase().newBatchUpdate()
+ refDb.newBatchUpdate()
.addCommand(rc1, rc2)
.setAtomic(true)
.setRefLogIdent(person)
@@ -194,15 +280,17 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
.execute(rw, NullProgressMonitor.INSTANCE);
}
- assertEquals(rc1.getResult(), ReceiveCommand.Result.OK);
- assertEquals(rc2.getResult(), ReceiveCommand.Result.OK);
+ assertEquals(ReceiveCommand.Result.OK, rc1.getResult());
+ assertEquals(ReceiveCommand.Result.OK, rc2.getResult());
- ReflogEntry e = db.getReflogReader("refs/heads/batch1").getLastEntry();
+ ReflogEntry e = refDb.getReflogReader("refs/heads/batch1")
+ .getLastEntry();
assertEquals(msg, e.getComment());
assertEquals(person, e.getWho());
assertEquals(cur, e.getNewId());
- e = db.getReflogReader("refs/heads/batch2").getLastEntry();
+ e = refDb.getReflogReader("refs/heads/batch2")
+ .getLastEntry();
assertEquals(msg, e.getComment());
assertEquals(person, e.getWho());
assertEquals(prev, e.getNewId());
@@ -267,7 +355,7 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
RefUpdate up = db.getRefDatabase().newUpdate("refs/heads/a", false);
up.setForceUpdate(true);
RefUpdate.Result res = up.delete();
- assertEquals(res, FORCED);
+ assertEquals(FORCED, res);
assertNull(db.exactRef("refs/heads/a"));
}
@@ -309,7 +397,7 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
// the branch HEAD referred to is left untouched
assertEquals(pid, db.resolve("refs/heads/master"));
- ReflogReader reflogReader = db.getReflogReader("HEAD");
+ ReflogReader reflogReader = db.getRefDatabase().getReflogReader("HEAD");
ReflogEntry e = reflogReader.getReverseEntries().get(0);
assertEquals(ppid, e.getNewId());
assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
@@ -330,12 +418,13 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
updateRef.setForceUpdate(true);
RefUpdate.Result update = updateRef.update();
assertEquals(FORCED, update); // internal
- ReflogReader r = db.getReflogReader("refs/heads/master");
+ ReflogReader r = db.getRefDatabase()
+ .getReflogReader("refs/heads/master");
ReflogEntry e = r.getLastEntry();
- assertEquals(e.getNewId(), pid);
- assertEquals(e.getComment(), "REFLOG!: FORCED");
- assertEquals(e.getWho(), person);
+ assertEquals(pid, e.getNewId());
+ assertEquals("REFLOG!: FORCED", e.getComment());
+ assertEquals(person, e.getWho());
}
@Test
@@ -352,10 +441,11 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
ref = db.updateRef(newRef);
ref.setNewObjectId(db.resolve(Constants.HEAD));
- assertEquals(ref.delete(), RefUpdate.Result.NO_CHANGE);
+ assertEquals(RefUpdate.Result.NO_CHANGE, ref.delete());
// Differs from RefupdateTest. Deleting a loose ref leaves reflog trail.
- ReflogReader reader = db.getReflogReader("refs/heads/abc");
+ ReflogReader reader = db.getRefDatabase()
+ .getReflogReader("refs/heads/abc");
assertEquals(ObjectId.zeroId(), reader.getReverseEntry(1).getOldId());
assertEquals(nonZero, reader.getReverseEntry(1).getNewId());
assertEquals(nonZero, reader.getReverseEntry(0).getOldId());
@@ -382,8 +472,9 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
assertNotSame(newid, r.getObjectId());
assertSame(ObjectId.class, r.getObjectId().getClass());
assertEquals(newid, r.getObjectId());
- List<ReflogEntry> reverseEntries1 = db.getReflogReader("refs/heads/abc")
- .getReverseEntries();
+ RefDatabase refDb = db.getRefDatabase();
+ List<ReflogEntry> reverseEntries1 = refDb
+ .getReflogReader("refs/heads/abc").getReverseEntries();
ReflogEntry entry1 = reverseEntries1.get(0);
assertEquals(1, reverseEntries1.size());
assertEquals(ObjectId.zeroId(), entry1.getOldId());
@@ -392,7 +483,7 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
assertEquals(new PersonIdent(db).toString(),
entry1.getWho().toString());
assertEquals("", entry1.getComment());
- List<ReflogEntry> reverseEntries2 = db.getReflogReader("HEAD")
+ List<ReflogEntry> reverseEntries2 = refDb.getReflogReader("HEAD")
.getReverseEntries();
assertEquals(0, reverseEntries2.size());
}
@@ -431,7 +522,7 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
Ref head = db.exactRef("HEAD");
assertTrue(head.isSymbolic());
- assertEquals(head.getTarget().getName(), "refs/heads/unborn");
+ assertEquals("refs/heads/unborn", head.getTarget().getName());
}
/**
@@ -455,7 +546,7 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
// the branch HEAD referred to is left untouched
assertNull(db.resolve("refs/heads/unborn"));
- ReflogReader reflogReader = db.getReflogReader("HEAD");
+ ReflogReader reflogReader = db.getRefDatabase().getReflogReader("HEAD");
ReflogEntry e = reflogReader.getReverseEntries().get(0);
assertEquals(ObjectId.zeroId(), e.getOldId());
assertEquals(ppid, e.getNewId());
@@ -499,7 +590,7 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
names.add("refs/heads/new/name");
for (String nm : names) {
- ReflogReader rd = db.getReflogReader(nm);
+ ReflogReader rd = db.getRefDatabase().getReflogReader(nm);
assertNotNull(rd);
ReflogEntry last = rd.getLastEntry();
ObjectId id = last.getNewId();
@@ -573,10 +664,10 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
assertTrue(res == Result.NEW || res == FORCED);
}
- assertEquals(refDb.exactRef(refName).getObjectId(), bId);
+ assertEquals(bId, refDb.exactRef(refName).getObjectId());
assertTrue(randomStr.equals(refDb.getReflogReader(refName).getReverseEntry(1).getComment()));
refDb.compactFully();
- assertEquals(refDb.exactRef(refName).getObjectId(), bId);
+ assertEquals(bId, refDb.exactRef(refName).getObjectId());
assertTrue(randomStr.equals(refDb.getReflogReader(refName).getReverseEntry(1).getComment()));
}
@@ -644,6 +735,54 @@ public class FileReftableTest extends SampleDataRepositoryTestCase {
checkContainsRef(refs, db.exactRef("HEAD"));
}
+ @Test
+ public void testExternalUpdate_bug_102() throws Exception {
+ ((FileReftableDatabase) db.getRefDatabase()).setAutoRefresh(true);
+ assumeTrue(atLeastGitVersion(2, 45));
+ Git git = Git.wrap(db);
+ git.tag().setName("foo").call();
+ Ref ref = db.exactRef("refs/tags/foo");
+ assertNotNull(ref);
+ runGitCommand("tag", "--force", "foo", "e");
+ Ref e = db.exactRef("refs/heads/e");
+ Ref foo = db.exactRef("refs/tags/foo");
+ assertEquals(e.getObjectId(), foo.getObjectId());
+ }
+
+ private String toString(TemporaryBuffer b) throws IOException {
+ return RawParseUtils.decode(b.toByteArray());
+ }
+
+ private ExecutionResult runGitCommand(String... args)
+ throws IOException, InterruptedException {
+ FS fs = db.getFS();
+ ProcessBuilder pb = fs.runInShell("git", args);
+ pb.directory(db.getWorkTree());
+ System.err.println("PATH=" + pb.environment().get("PATH"));
+ ExecutionResult result = fs.execute(pb, null);
+ assertEquals(0, result.getRc());
+ String err = toString(result.getStderr());
+ if (!err.isEmpty()) {
+ System.err.println(err);
+ }
+ String out = toString(result.getStdout());
+ if (!out.isEmpty()) {
+ System.out.println(out);
+ }
+ return result;
+ }
+
+ private boolean atLeastGitVersion(int minMajor, int minMinor)
+ throws IOException, InterruptedException {
+ String version = toString(runGitCommand("version").getStdout())
+ .split(" ")[2];
+ System.out.println(version);
+ String[] digits = version.split("\\.");
+ int major = Integer.parseInt(digits[0]);
+ int minor = Integer.parseInt(digits[1]);
+ return (major >= minMajor) && (minor >= minMinor);
+ }
+
private RefUpdate updateRef(String name) throws IOException {
final RefUpdate ref = db.updateRef(name);
ref.setNewObjectId(db.resolve(Constants.HEAD));
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 6cad8b6c62..434f7e4bef 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
@@ -16,9 +16,9 @@ import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Date;
import java.util.List;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
@@ -206,7 +206,7 @@ public class GcBasicPackingTest extends GcTestCase {
// The old packfile is too young to be deleted. We should end up with
// two pack files
- gc.setExpire(new Date(oldPackfile.lastModified() - 1));
+ gc.setExpire(Instant.ofEpochMilli(oldPackfile.lastModified() - 1));
gc.gc().get();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcNumberOfPackFilesSinceBitmapStatisticsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcNumberOfPackFilesSinceBitmapStatisticsTest.java
new file mode 100644
index 0000000000..cd1264ef55
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcNumberOfPackFilesSinceBitmapStatisticsTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2024 Jacek Centkowski <geminica.programs@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
+ * 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.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.stream.StreamSupport;
+
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+public class GcNumberOfPackFilesSinceBitmapStatisticsTest extends GcTestCase {
+ @Test
+ public void testShouldReportZeroObjectsForInitializedRepo()
+ throws IOException {
+ assertEquals(0L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportAllPackFilesWhenNoGcWasPerformed()
+ throws Exception {
+ tr.packAndPrune();
+ long result = gc.getStatistics().numberOfPackFilesSinceBitmap;
+
+ assertEquals(repo.getObjectDatabase().getPacks().size(), result);
+ }
+
+ @Test
+ public void testShouldReportNoObjectsDirectlyAfterGc() throws Exception {
+ // given
+ addCommit(null);
+ gc.gc().get();
+ assertEquals(1L, repositoryBitmapFiles());
+ assertEquals(0L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNewObjectsSinceGcWhenRepositoryProgresses()
+ throws Exception {
+ // commit & gc
+ RevCommit parent = addCommit(null);
+ gc.gc().get();
+ assertEquals(1L, repositoryBitmapFiles());
+
+ // progress & pack
+ addCommit(parent);
+ tr.packAndPrune();
+
+ assertEquals(1L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNewObjectsFromTheLatestBitmapWhenRepositoryProgresses()
+ throws Exception {
+ // commit & gc
+ RevCommit parent = addCommit(null);
+ gc.gc().get();
+ assertEquals(1L, repositoryBitmapFiles());
+
+ // progress & gc
+ parent = addCommit(parent);
+ gc.gc().get();
+ assertEquals(2L, repositoryBitmapFiles());
+
+ // progress & pack
+ addCommit(parent);
+ tr.packAndPrune();
+
+ assertEquals(1L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ private RevCommit addCommit(RevCommit parent) throws Exception {
+ PersonIdent ident = new PersonIdent("repo-metrics", "repo@metrics.com");
+ TestRepository<FileRepository>.CommitBuilder builder = tr.commit()
+ .author(ident);
+ if (parent != null) {
+ builder.parent(parent);
+ }
+ RevCommit commit = builder.create();
+ tr.update("master", commit);
+ parent = commit;
+ return parent;
+ }
+
+ private long repositoryBitmapFiles() throws IOException {
+ return StreamSupport
+ .stream(Files
+ .newDirectoryStream(repo.getObjectDatabase()
+ .getPackDirectory().toPath(), "pack-*.bitmap")
+ .spliterator(), false)
+ .count();
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
index 8baa3cc341..f84be21e82 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
@@ -19,7 +19,6 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import java.io.File;
-import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.BrokenBarrierException;
@@ -31,6 +30,8 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.PackRefsCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
@@ -49,7 +50,7 @@ public class GcPackRefsTest extends GcTestCase {
RevBlob a = tr.blob("a");
tr.lightweightTag("t", a);
- gc.packRefs();
+ packRefs(false);
assertSame(repo.exactRef("refs/tags/t").getStorage(), Storage.PACKED);
}
@@ -58,9 +59,9 @@ public class GcPackRefsTest extends GcTestCase {
String ref = "dir/ref";
tr.branch(ref).commit().create();
String name = repo.findRef(ref).getName();
- Path dir = repo.getDirectory().toPath().resolve(name).getParent();
+ Path dir = repo.getCommonDirectory().toPath().resolve(name).getParent();
assertNotNull(dir);
- gc.packRefs();
+ packRefs(true);
assertFalse(Files.exists(dir));
}
@@ -75,9 +76,9 @@ public class GcPackRefsTest extends GcTestCase {
Callable<Integer> packRefs = () -> {
syncPoint.await();
try {
- gc.packRefs();
+ packRefs(false);
return 0;
- } catch (IOException e) {
+ } catch (GitAPIException e) {
return 1;
}
};
@@ -102,7 +103,7 @@ public class GcPackRefsTest extends GcTestCase {
"refs/tags/t1"));
try {
refLock.lock();
- gc.packRefs();
+ packRefs(false);
} finally {
refLock.unlock();
}
@@ -145,7 +146,7 @@ public class GcPackRefsTest extends GcTestCase {
Future<Result> result2 = pool.submit(() -> {
refUpdateLockedRef.await();
- gc.packRefs();
+ packRefs(false);
packRefsDone.await();
return null;
});
@@ -173,19 +174,20 @@ public class GcPackRefsTest extends GcTestCase {
assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
- gc.packRefs();
+ PackRefsCommand packRefsCommand = git.packRefs().setAll(true);
+ packRefsCommand.call();
assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
git.checkout().setName("refs/heads/side").call();
- gc.packRefs();
+ packRefsCommand.call();
assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
// check for detached HEAD
git.checkout().setName(first.getName()).call();
- gc.packRefs();
+ packRefsCommand.call();
assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
}
@@ -208,7 +210,7 @@ public class GcPackRefsTest extends GcTestCase {
assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
- gc.packRefs();
+ packRefs(true);
assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
@@ -216,9 +218,14 @@ public class GcPackRefsTest extends GcTestCase {
// check for non-detached HEAD
repo.updateRef(Constants.HEAD).link("refs/heads/side");
- gc.packRefs();
+ packRefs(true);
assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
assertEquals(repo.exactRef("HEAD").getTarget().getObjectId(),
second.getId());
}
+
+ private void packRefs(boolean all) throws GitAPIException {
+ new PackRefsCommand(repo).setAll(all).call();
+ }
+
}
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 ca0f6842fc..84ec132e24 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
@@ -16,8 +16,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
+import java.time.Instant;
import java.util.Collections;
-import java.util.Date;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.lib.ObjectId;
@@ -30,7 +30,7 @@ public class GcPruneNonReferencedTest extends GcTestCase {
@Test
public void nonReferencedNonExpiredObject_notPruned() throws Exception {
RevBlob a = tr.blob("a");
- gc.setExpire(new Date(lastModified(a)));
+ gc.setExpire(Instant.ofEpochMilli(lastModified(a)));
gc.prune(Collections.<ObjectId> emptySet());
assertTrue(repo.getObjectDatabase().has(a));
}
@@ -58,7 +58,7 @@ public class GcPruneNonReferencedTest extends GcTestCase {
@Test
public void nonReferencedObjects_onlyExpiredPruned() throws Exception {
RevBlob a = tr.blob("a");
- gc.setExpire(new Date(lastModified(a) + 1));
+ gc.setExpire(Instant.ofEpochMilli(lastModified(a) + 1));
fsTick();
RevBlob b = tr.blob("b");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
index e6c1ee5fd6..29f180d76b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
@@ -30,7 +30,7 @@ public class GcReflogTest extends GcTestCase {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
- new File(repo.getDirectory(), Constants.LOGS + "/refs/heads/master")
+ new File(repo.getCommonDirectory(), Constants.LOGS + "/refs/heads/master")
.delete();
stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcSinceBitmapStatisticsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcSinceBitmapStatisticsTest.java
new file mode 100644
index 0000000000..af52e2cb85
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcSinceBitmapStatisticsTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2024 Jacek Centkowski <geminica.programs@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
+ * 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.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Collection;
+import java.util.stream.StreamSupport;
+
+import org.eclipse.jgit.internal.storage.file.GC.RepoStatistics;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.junit.Test;
+
+public class GcSinceBitmapStatisticsTest extends GcTestCase {
+ @Test
+ public void testShouldReportZeroPacksAndObjectsForInitializedRepo()
+ throws IOException {
+ RepoStatistics s = gc.getStatistics();
+ assertEquals(0L, s.numberOfPackFilesSinceBitmap);
+ assertEquals(0L, s.numberOfObjectsSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportAllPackFilesWhenNoGcWasPerformed()
+ throws Exception {
+ tr.packAndPrune();
+ long result = gc.getStatistics().numberOfPackFilesSinceBitmap;
+
+ assertEquals(repo.getObjectDatabase().getPacks().size(), result);
+ }
+
+ @Test
+ public void testShouldReportAllObjectsWhenNoGcWasPerformed()
+ throws Exception {
+ tr.packAndPrune();
+
+ assertEquals(
+ getNumberOfObjectsInPacks(repo.getObjectDatabase().getPacks()),
+ gc.getStatistics().numberOfObjectsSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNoPacksFilesSinceBitmapWhenPackfilesAreOlderThanBitmapFile()
+ throws Exception {
+ addCommit(null);
+ configureGC(/* buildBitmap */ false).gc().get();
+ assertEquals(1L, gc.getStatistics().numberOfPackFiles);
+ assertEquals(0L, repositoryBitmapFiles());
+ assertEquals(1L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+
+ addCommit(null);
+ configureGC(/* buildBitmap */ true).gc().get();
+
+ assertEquals(1L, repositoryBitmapFiles());
+ assertEquals(2L, gc.getStatistics().numberOfPackFiles);
+ assertEquals(0L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNoObjectsDirectlyAfterGc() throws Exception {
+ // given
+ addCommit(null);
+ assertEquals(2L, gc.getStatistics().numberOfObjectsSinceBitmap);
+
+ gc.gc().get();
+ assertEquals(0L, gc.getStatistics().numberOfObjectsSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNewPacksSinceGcWhenRepositoryProgresses()
+ throws Exception {
+ // commit & gc
+ RevCommit parent = addCommit(null);
+ gc.gc().get();
+ assertEquals(1L, repositoryBitmapFiles());
+
+ // progress & pack
+ addCommit(parent);
+ assertEquals(1L, gc.getStatistics().numberOfPackFiles);
+ assertEquals(0L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+
+ tr.packAndPrune();
+ assertEquals(2L, gc.getStatistics().numberOfPackFiles);
+ assertEquals(1L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNewObjectsSinceGcWhenRepositoryProgresses()
+ throws Exception {
+ // commit & gc
+ RevCommit parent = addCommit(null);
+ gc.gc().get();
+ assertEquals(0L, gc.getStatistics().numberOfLooseObjects);
+ assertEquals(0L, gc.getStatistics().numberOfObjectsSinceBitmap);
+
+ // progress & pack
+ addCommit(parent);
+ assertEquals(1L, gc.getStatistics().numberOfLooseObjects);
+ assertEquals(1L, gc.getStatistics().numberOfObjectsSinceBitmap);
+
+ tr.packAndPrune();
+ assertEquals(0L, gc.getStatistics().numberOfLooseObjects);
+ // Number of objects contained in the newly created PackFile
+ assertEquals(3L, gc.getStatistics().numberOfObjectsSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNewPacksFromTheLatestBitmapWhenRepositoryProgresses()
+ throws Exception {
+ // commit & gc
+ RevCommit parent = addCommit(null);
+ gc.gc().get();
+ assertEquals(1L, repositoryBitmapFiles());
+
+ // progress & gc
+ parent = addCommit(parent);
+ gc.gc().get();
+ assertEquals(2L, repositoryBitmapFiles());
+
+ // progress & pack
+ addCommit(parent);
+ tr.packAndPrune();
+
+ assertEquals(1L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNewObjectsFromTheLatestBitmapWhenRepositoryProgresses()
+ throws Exception {
+ // commit & gc
+ RevCommit parent = addCommit(null);
+ gc.gc().get();
+
+ // progress & gc
+ parent = addCommit(parent);
+ gc.gc().get();
+ assertEquals(0L, gc.getStatistics().numberOfObjectsSinceBitmap);
+
+ // progress & pack
+ addCommit(parent);
+ assertEquals(1L, gc.getStatistics().numberOfObjectsSinceBitmap);
+
+ tr.packAndPrune();
+ assertEquals(4L, gc.getStatistics().numberOfObjectsSinceBitmap);
+ }
+
+ private RevCommit addCommit(RevCommit parent) throws Exception {
+ return tr.branch("master").commit()
+ .author(new PersonIdent("repo-metrics", "repo@metrics.com"))
+ .parent(parent).create();
+ }
+
+ private long repositoryBitmapFiles() throws IOException {
+ return StreamSupport
+ .stream(Files
+ .newDirectoryStream(repo.getObjectDatabase()
+ .getPackDirectory().toPath(), "pack-*.bitmap")
+ .spliterator(), false)
+ .count();
+ }
+
+ private long getNumberOfObjectsInPacks(Collection<Pack> packs) {
+ return packs.stream().mapToLong(pack -> {
+ try {
+ return pack.getObjectCount();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }).sum();
+ }
+
+ private GC configureGC(boolean buildBitmap) {
+ PackConfig pc = new PackConfig(repo.getObjectDatabase().getConfig());
+ pc.setBuildBitmaps(buildBitmap);
+ gc.setPackConfig(pc);
+ return gc;
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
index 746a0a1ff3..33cbc868ca 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
@@ -49,7 +49,10 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import java.io.File;
@@ -66,6 +69,7 @@ import java.util.concurrent.Future;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@@ -207,33 +211,35 @@ public class ObjectDirectoryTest extends RepositoryTestCase {
.fromString("873fb8d667d05436d728c52b1d7a09528e6eb59b");
WindowCursor curs = new WindowCursor(db.getObjectDatabase());
- LooseObjects mock = mock(LooseObjects.class);
+ Config config = new Config();
+ config.setString("core", null, "trustLooseObjectStat", "ALWAYS");
+ LooseObjects spy = Mockito.spy(new LooseObjects(config, trash));
UnpackedObjectCache unpackedObjectCacheMock = mock(
UnpackedObjectCache.class);
- Mockito.when(mock.getObjectLoader(any(), any(), any()))
- .thenThrow(new IOException("Stale File Handle"));
- Mockito.when(mock.open(curs, id)).thenCallRealMethod();
- Mockito.when(mock.unpackedObjectCache())
- .thenReturn(unpackedObjectCacheMock);
+ doThrow(new IOException("Stale File Handle")).when(spy)
+ .getObjectLoader(any(), any(), any());
+ doReturn(unpackedObjectCacheMock).when(spy).unpackedObjectCache();
- assertNull(mock.open(curs, id));
+ assertNull(spy.open(curs, id));
verify(unpackedObjectCacheMock).remove(id);
}
- @Test
+ @Test(expected = IOException.class)
public void testOpenLooseObjectPropagatesIOExceptions() throws Exception {
ObjectId id = ObjectId
.fromString("873fb8d667d05436d728c52b1d7a09528e6eb59b");
WindowCursor curs = new WindowCursor(db.getObjectDatabase());
- LooseObjects mock = mock(LooseObjects.class);
+ Config config = new Config();
+ config.setString("core", null, "trustLooseObjectStat", "NEVER");
+ LooseObjects spy = spy(new LooseObjects(config,
+ db.getObjectDatabase().getDirectory()));
- Mockito.when(mock.getObjectLoader(any(), any(), any()))
- .thenThrow(new IOException("some IO failure"));
- Mockito.when(mock.open(curs, id)).thenCallRealMethod();
+ doThrow(new IOException("some IO failure")).when(spy)
+ .getObjectLoader(any(), any(), any());
- assertThrows(IOException.class, () -> mock.open(curs, id));
+ spy.open(curs, id);
}
@Test
@@ -243,17 +249,18 @@ public class ObjectDirectoryTest extends RepositoryTestCase {
db.getConfig().setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, true);
- WindowCursor curs = new WindowCursor(db.getObjectDatabase());
- assertTrue(curs.getCommitGraph().isEmpty());
- commitFile("file.txt", "content", "master");
- GC gc = new GC(db);
- gc.gc().get();
- assertTrue(curs.getCommitGraph().isPresent());
+ try (WindowCursor curs = new WindowCursor(db.getObjectDatabase())) {
+ assertTrue(curs.getCommitGraph().isEmpty());
+ commitFile("file.txt", "content", "master");
+ GC gc = new GC(db);
+ gc.gc().get();
+ assertTrue(curs.getCommitGraph().isPresent());
- db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_COMMIT_GRAPH, false);
+ db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_COMMIT_GRAPH, false);
- assertTrue(curs.getCommitGraph().isEmpty());
+ assertTrue(curs.getCommitGraph().isEmpty());
+ }
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
index 24bdc4a97a..1f934acced 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
@@ -13,6 +13,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.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
@@ -25,6 +26,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
@@ -99,6 +101,39 @@ public abstract class PackIndexTestCase extends RepositoryTestCase {
}
}
+ @Test
+ public void testIteratorMutableEntryCompareTo() {
+ Iterator<PackIndex.MutableEntry> iterA = smallIdx.iterator();
+ Iterator<PackIndex.MutableEntry> iterB = smallIdx.iterator();
+
+ MutableEntry aEntry = iterA.next();
+ iterB.next();
+ MutableEntry bEntry = iterB.next();
+ // b is one ahead
+ assertTrue(aEntry.compareBySha1To(bEntry) < 0);
+ assertTrue(bEntry.compareBySha1To(aEntry) > 0);
+
+ // advance a, now should be equal
+ assertEquals(0, iterA.next().compareBySha1To(bEntry));
+ }
+
+ @Test
+ public void testIteratorMutableEntryCopyTo() {
+ Iterator<PackIndex.MutableEntry> it = smallIdx.iterator();
+
+ MutableObjectId firstOidCopy = new MutableObjectId();
+ MutableEntry next = it.next();
+ next.copyOidTo(firstOidCopy);
+ ObjectId firstImmutable = next.toObjectId();
+
+ MutableEntry second = it.next();
+
+ // The copy has the right value after "next"
+ assertTrue(firstImmutable.equals(firstOidCopy));
+ assertFalse("iterator has moved",
+ second.toObjectId().equals(firstImmutable));
+ }
+
/**
* Test results of iterator comparing to content of well-known (prepared)
* small index.
@@ -106,22 +141,22 @@ public abstract class PackIndexTestCase extends RepositoryTestCase {
@Test
public void testIteratorReturnedValues1() {
Iterator<PackIndex.MutableEntry> iter = smallIdx.iterator();
- assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", iter.next()
- .name());
- assertEquals("540a36d136cf413e4b064c2b0e0a4db60f77feab", iter.next()
- .name());
- assertEquals("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259", iter.next()
- .name());
- assertEquals("6ff87c4664981e4397625791c8ea3bbb5f2279a3", iter.next()
- .name());
- assertEquals("82c6b885ff600be425b4ea96dee75dca255b69e7", iter.next()
- .name());
- assertEquals("902d5476fa249b7abc9d84c611577a81381f0327", iter.next()
- .name());
- assertEquals("aabf2ffaec9b497f0950352b3e582d73035c2035", iter.next()
- .name());
- assertEquals("c59759f143fb1fe21c197981df75a7ee00290799", iter.next()
- .name());
+ assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904",
+ iter.next().name());
+ assertEquals("540a36d136cf413e4b064c2b0e0a4db60f77feab",
+ iter.next().name());
+ assertEquals("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259",
+ iter.next().name());
+ assertEquals("6ff87c4664981e4397625791c8ea3bbb5f2279a3",
+ iter.next().name());
+ assertEquals("82c6b885ff600be425b4ea96dee75dca255b69e7",
+ iter.next().name());
+ assertEquals("902d5476fa249b7abc9d84c611577a81381f0327",
+ iter.next().name());
+ assertEquals("aabf2ffaec9b497f0950352b3e582d73035c2035",
+ iter.next().name());
+ assertEquals("c59759f143fb1fe21c197981df75a7ee00290799",
+ iter.next().name());
assertFalse(iter.hasNext());
}
@@ -198,16 +233,16 @@ public abstract class PackIndexTestCase extends RepositoryTestCase {
@Test
public void testIteratorReturnedValues2() {
Iterator<PackIndex.MutableEntry> iter = denseIdx.iterator();
- while (!iter.next().name().equals(
- "0a3d7772488b6b106fb62813c4d6d627918d9181")) {
+ while (!iter.next().name()
+ .equals("0a3d7772488b6b106fb62813c4d6d627918d9181")) {
// just iterating
}
- assertEquals("1004d0d7ac26fbf63050a234c9b88a46075719d3", iter.next()
- .name()); // same level-1
- assertEquals("10da5895682013006950e7da534b705252b03be6", iter.next()
- .name()); // same level-1
- assertEquals("1203b03dc816ccbb67773f28b3c19318654b0bc8", iter.next()
- .name());
+ assertEquals("1004d0d7ac26fbf63050a234c9b88a46075719d3",
+ iter.next().name()); // same level-1
+ assertEquals("10da5895682013006950e7da534b705252b03be6",
+ iter.next().name()); // same level-1
+ assertEquals("1203b03dc816ccbb67773f28b3c19318654b0bc8",
+ iter.next().name());
}
@Test
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 2bafde65d3..baa0182b87 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
@@ -90,25 +90,26 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
@Test
public void testCreate() throws IOException {
// setUp above created the directory. We just have to test it.
- File d = diskRepo.getDirectory();
+ File gitDir = diskRepo.getDirectory();
+ File commonDir = diskRepo.getCommonDirectory();
assertSame(diskRepo, refdir.getRepository());
- assertTrue(new File(d, "refs").isDirectory());
- assertTrue(new File(d, "logs").isDirectory());
- assertTrue(new File(d, "logs/refs").isDirectory());
- assertFalse(new File(d, "packed-refs").exists());
+ assertTrue(new File(commonDir, "refs").isDirectory());
+ assertTrue(new File(commonDir, "logs").isDirectory());
+ assertTrue(new File(commonDir, "logs/refs").isDirectory());
+ assertFalse(new File(commonDir, "packed-refs").exists());
- assertTrue(new File(d, "refs/heads").isDirectory());
- assertTrue(new File(d, "refs/tags").isDirectory());
- assertEquals(2, new File(d, "refs").list().length);
- assertEquals(0, new File(d, "refs/heads").list().length);
- assertEquals(0, new File(d, "refs/tags").list().length);
+ assertTrue(new File(commonDir, "refs/heads").isDirectory());
+ assertTrue(new File(commonDir, "refs/tags").isDirectory());
+ assertEquals(2, new File(commonDir, "refs").list().length);
+ assertEquals(0, new File(commonDir, "refs/heads").list().length);
+ assertEquals(0, new File(commonDir, "refs/tags").list().length);
- assertTrue(new File(d, "logs/refs/heads").isDirectory());
- assertFalse(new File(d, "logs/HEAD").exists());
- assertEquals(0, new File(d, "logs/refs/heads").list().length);
+ assertTrue(new File(commonDir, "logs/refs/heads").isDirectory());
+ assertFalse(new File(gitDir, "logs/HEAD").exists());
+ assertEquals(0, new File(commonDir, "logs/refs/heads").list().length);
- assertEquals("ref: refs/heads/master\n", read(new File(d, HEAD)));
+ assertEquals("ref: refs/heads/master\n", read(new File(gitDir, HEAD)));
}
@Test(expected = UnsupportedOperationException.class)
@@ -1382,7 +1383,7 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
}
private void deleteLooseRef(String name) {
- File path = new File(diskRepo.getDirectory(), name);
+ File path = new File(diskRepo.getCommonDirectory(), name);
assertTrue("deleted " + name, path.delete());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
index cb977bd601..acc36d76f4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
@@ -40,6 +40,7 @@ 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.RefDatabase;
import org.eclipse.jgit.lib.RefRename;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
@@ -111,16 +112,17 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertNotSame(newid, r.getObjectId());
assertSame(ObjectId.class, r.getObjectId().getClass());
assertEquals(newid, r.getObjectId());
- List<ReflogEntry> reverseEntries1 = db
+ List<ReflogEntry> reverseEntries1 = db.getRefDatabase()
.getReflogReader("refs/heads/abc").getReverseEntries();
ReflogEntry entry1 = reverseEntries1.get(0);
assertEquals(1, reverseEntries1.size());
assertEquals(ObjectId.zeroId(), entry1.getOldId());
assertEquals(r.getObjectId(), entry1.getNewId());
- assertEquals(new PersonIdent(db).toString(), entry1.getWho().toString());
+ assertEquals(new PersonIdent(db).toString(),
+ entry1.getWho().toString());
assertEquals("", entry1.getComment());
- List<ReflogEntry> reverseEntries2 = db.getReflogReader("HEAD")
- .getReverseEntries();
+ List<ReflogEntry> reverseEntries2 = db.getRefDatabase()
+ .getReflogReader("HEAD").getReverseEntries();
assertEquals(0, reverseEntries2.size());
}
@@ -136,8 +138,11 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
final RefUpdate ru2 = updateRef(newRef2);
Result update2 = ru2.update();
assertEquals(Result.LOCK_FAILURE, update2);
- assertEquals(1, db.getReflogReader("refs/heads/z").getReverseEntries().size());
- assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(1, refDb.getReflogReader("refs/heads/z")
+ .getReverseEntries().size());
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
}
@Test
@@ -147,8 +152,10 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
final RefUpdate ru = updateRef(newRef);
Result update = ru.update();
assertEquals(Result.LOCK_FAILURE, update);
- assertNull(db.getReflogReader("refs/heads/master/x"));
- assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertNull(refDb.getReflogReader("refs/heads/master/x"));
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
}
@Test
@@ -163,9 +170,12 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
final RefUpdate ru2 = updateRef(newRef2);
Result update2 = ru2.update();
assertEquals(Result.LOCK_FAILURE, update2);
- assertEquals(1, db.getReflogReader("refs/heads/z/a").getReverseEntries().size());
- assertNull(db.getReflogReader("refs/heads/z"));
- assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(1, refDb.getReflogReader("refs/heads/z/a")
+ .getReverseEntries().size());
+ assertNull(refDb.getReflogReader("refs/heads/z"));
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
}
@Test
@@ -175,8 +185,10 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
final RefUpdate ru = updateRef(newRef);
Result update = ru.update();
assertEquals(Result.LOCK_FAILURE, update);
- assertNull(db.getReflogReader("refs/heads/prefix"));
- assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertNull(refDb.getReflogReader("refs/heads/prefix"));
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
}
/**
@@ -197,8 +209,11 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
Result delete = updateRef2.delete();
assertEquals(Result.REJECTED_CURRENT_BRANCH, delete);
assertEquals(pid, db.resolve("refs/heads/master"));
- assertEquals(1,db.getReflogReader("refs/heads/master").getReverseEntries().size());
- assertEquals(0,db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(1, refDb.getReflogReader("refs/heads/master")
+ .getReverseEntries().size());
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
}
@Test
@@ -209,7 +224,8 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
updateRef.setForceUpdate(true);
Result update = updateRef.update();
assertEquals(Result.FORCED, update);
- assertEquals(1,db.getReflogReader("refs/heads/master").getReverseEntries().size());
+ assertEquals(1, db.getRefDatabase().getReflogReader("refs/heads/master")
+ .getReverseEntries().size());
}
@Test
@@ -219,15 +235,18 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
ref.update(); // create loose ref
ref = updateRef(newRef); // refresh
delete(ref, Result.NO_CHANGE);
- assertNull(db.getReflogReader("refs/heads/abc"));
+ assertNull(db.getRefDatabase().getReflogReader("refs/heads/abc"));
}
@Test
public void testDeleteHead() throws IOException {
final RefUpdate ref = updateRef(Constants.HEAD);
delete(ref, Result.REJECTED_CURRENT_BRANCH, true, false);
- assertEquals(0, db.getReflogReader("refs/heads/master").getReverseEntries().size());
- assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(0, refDb.getReflogReader("refs/heads/master")
+ .getReverseEntries().size());
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
}
@Test
@@ -423,7 +442,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
// the branch HEAD referred to is left untouched
assertEquals(pid, db.resolve("refs/heads/master"));
- ReflogReader reflogReader = db.getReflogReader("HEAD");
+ ReflogReader reflogReader = db.getRefDatabase().getReflogReader("HEAD");
ReflogEntry e = reflogReader.getReverseEntries().get(0);
assertEquals(pid, e.getOldId());
assertEquals(ppid, e.getNewId());
@@ -453,7 +472,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
// the branch HEAD referred to is left untouched
assertNull(db.resolve("refs/heads/unborn"));
- ReflogReader reflogReader = db.getReflogReader("HEAD");
+ ReflogReader reflogReader = db.getRefDatabase().getReflogReader("HEAD");
ReflogEntry e = reflogReader.getReverseEntries().get(0);
assertEquals(ObjectId.zeroId(), e.getOldId());
assertEquals(ppid, e.getNewId());
@@ -691,9 +710,12 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals(Result.RENAMED, result);
assertEquals(rb, db.resolve("refs/heads/new/name"));
assertNull(db.resolve("refs/heads/b"));
- assertEquals(1, db.getReflogReader("new/name").getReverseEntries().size());
- assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name")
- .getLastEntry().getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(1, refDb.getReflogReader("refs/heads/new/name")
+ .getReverseEntries().size());
+ assertEquals("Branch: renamed b to new/name",
+ refDb.getReflogReader("refs/heads/new/name").getLastEntry()
+ .getComment());
assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged
}
@@ -713,11 +735,15 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals(Result.RENAMED, result);
assertEquals(rb, db.resolve("refs/heads/new/name"));
assertNull(db.resolve("refs/heads/b"));
- assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size());
- assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name")
- .getLastEntry().getComment());
- assertEquals("Just a message", db.getReflogReader("new/name")
- .getReverseEntries().get(1).getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(2, refDb.getReflogReader("refs/heads/new/name")
+ .getReverseEntries().size());
+ assertEquals("Branch: renamed b to new/name",
+ refDb.getReflogReader("refs/heads/new/name").getLastEntry()
+ .getComment());
+ assertEquals("Just a message",
+ refDb.getReflogReader("refs/heads/new/name").getReverseEntries()
+ .get(1).getComment());
assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged
}
@@ -737,13 +763,20 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals(Result.RENAMED, result);
assertEquals(rb, db.resolve("refs/heads/new/name"));
assertNull(db.resolve("refs/heads/b"));
- assertEquals("Branch: renamed b to new/name", db.getReflogReader(
- "new/name").getLastEntry().getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals("Branch: renamed b to new/name",
+ refDb.getReflogReader("refs/heads/new/name").getLastEntry()
+ .getComment());
assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
assertEquals(rb, db.resolve(Constants.HEAD));
- assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size());
- assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name").getReverseEntries().get(0).getComment());
- assertEquals("Just a message", db.getReflogReader("new/name").getReverseEntries().get(1).getComment());
+ assertEquals(2, refDb.getReflogReader("refs/heads/new/name")
+ .getReverseEntries().size());
+ assertEquals("Branch: renamed b to new/name",
+ refDb.getReflogReader("refs/heads/new/name").getReverseEntries()
+ .get(0).getComment());
+ assertEquals("Just a message",
+ refDb.getReflogReader("refs/heads/new/name").getReverseEntries()
+ .get(1).getComment());
}
@Test
@@ -766,11 +799,17 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals(Result.RENAMED, result);
assertEquals(rb2, db.resolve("refs/heads/new/name"));
assertNull(db.resolve("refs/heads/b"));
- assertEquals("Branch: renamed b to new/name", db.getReflogReader(
- "new/name").getLastEntry().getComment());
- assertEquals(3, db.getReflogReader("refs/heads/new/name").getReverseEntries().size());
- assertEquals("Branch: renamed b to new/name", db.getReflogReader("refs/heads/new/name").getReverseEntries().get(0).getComment());
- assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals("Branch: renamed b to new/name",
+ refDb.getReflogReader("refs/heads/new/name").getLastEntry()
+ .getComment());
+ assertEquals(3, refDb.getReflogReader("refs/heads/new/name")
+ .getReverseEntries().size());
+ assertEquals("Branch: renamed b to new/name",
+ refDb.getReflogReader("refs/heads/new/name").getReverseEntries()
+ .get(0).getComment());
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
// make sure b's log file is gone too.
assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
@@ -789,9 +828,10 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
ObjectId oldfromId = db.resolve(fromName);
ObjectId oldHeadId = db.resolve(Constants.HEAD);
writeReflog(db, oldfromId, "Just a message", fromName);
- List<ReflogEntry> oldFromLog = db
+ RefDatabase refDb = db.getRefDatabase();
+ List<ReflogEntry> oldFromLog = refDb
.getReflogReader(fromName).getReverseEntries();
- List<ReflogEntry> oldHeadLog = oldHeadId != null ? db
+ List<ReflogEntry> oldHeadLog = oldHeadId != null ? refDb
.getReflogReader(Constants.HEAD).getReverseEntries() : null;
assertTrue("internal check, we have a log", new File(db.getDirectory(),
@@ -818,10 +858,10 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals(oldHeadId, db.resolve(Constants.HEAD));
assertEquals(oldfromId, db.resolve(fromName));
assertNull(db.resolve(toName));
- assertEquals(oldFromLog.toString(), db.getReflogReader(fromName)
+ assertEquals(oldFromLog.toString(), refDb.getReflogReader(fromName)
.getReverseEntries().toString());
if (oldHeadId != null && oldHeadLog != null)
- assertEquals(oldHeadLog.toString(), db.getReflogReader(
+ assertEquals(oldHeadLog.toString(), refDb.getReflogReader(
Constants.HEAD).getReverseEntries().toString());
} finally {
lockFile.unlock();
@@ -942,15 +982,18 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals(Result.RENAMED, result);
assertNull(db.resolve("refs/heads/a"));
assertEquals(rb, db.resolve("refs/heads/a/b"));
- assertEquals(3, db.getReflogReader("a/b").getReverseEntries().size());
- assertEquals("Branch: renamed a to a/b", db.getReflogReader("a/b")
- .getReverseEntries().get(0).getComment());
- assertEquals("Just a message", db.getReflogReader("a/b")
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(3, refDb.getReflogReader("refs/heads/a/b")
+ .getReverseEntries().size());
+ assertEquals("Branch: renamed a to a/b",
+ refDb.getReflogReader("refs/heads/a/b").getReverseEntries()
+ .get(0).getComment());
+ assertEquals("Just a message", refDb.getReflogReader("refs/heads/a/b")
.getReverseEntries().get(1).getComment());
- assertEquals("Setup", db.getReflogReader("a/b").getReverseEntries()
- .get(2).getComment());
+ assertEquals("Setup", refDb.getReflogReader("refs/heads/a/b")
+ .getReverseEntries().get(2).getComment());
// same thing was logged to HEAD
- assertEquals("Branch: renamed a to a/b", db.getReflogReader("HEAD")
+ assertEquals("Branch: renamed a to a/b", refDb.getReflogReader("HEAD")
.getReverseEntries().get(0).getComment());
}
@@ -978,15 +1021,20 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertNull(db.resolve("refs/heads/prefix/a"));
assertEquals(rb, db.resolve("refs/heads/prefix"));
- assertEquals(3, db.getReflogReader("prefix").getReverseEntries().size());
- assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
- "prefix").getReverseEntries().get(0).getComment());
- assertEquals("Just a message", db.getReflogReader("prefix")
- .getReverseEntries().get(1).getComment());
- assertEquals("Setup", db.getReflogReader("prefix").getReverseEntries()
- .get(2).getComment());
- assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
- "HEAD").getReverseEntries().get(0).getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(3, refDb.getReflogReader("refs/heads/prefix")
+ .getReverseEntries().size());
+ assertEquals("Branch: renamed prefix/a to prefix",
+ refDb.getReflogReader("refs/heads/prefix").getReverseEntries()
+ .get(0).getComment());
+ assertEquals("Just a message",
+ refDb.getReflogReader("refs/heads/prefix").getReverseEntries()
+ .get(1).getComment());
+ assertEquals("Setup", refDb.getReflogReader("refs/heads/prefix")
+ .getReverseEntries().get(2).getComment());
+ assertEquals("Branch: renamed prefix/a to prefix",
+ refDb.getReflogReader("HEAD").getReverseEntries().get(0)
+ .getComment());
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
index dc0e749373..16645cbcd7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
@@ -27,6 +27,7 @@ import org.eclipse.jgit.lib.CheckoutEntry;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
@@ -154,18 +155,22 @@ public class ReflogReaderTest extends SampleDataRepositoryTestCase {
setupReflog("logs/refs/heads/a", aLine);
setupReflog("logs/refs/heads/master", masterLine);
setupReflog("logs/HEAD", headLine);
- assertEquals("branch: change to master", db.getReflogReader("master")
- .getLastEntry().getComment());
- assertEquals("branch: change to a", db.getReflogReader("a")
- .getLastEntry().getComment());
- assertEquals("branch: change to HEAD", db.getReflogReader("HEAD")
- .getLastEntry().getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals("branch: change to master",
+ refDb.getReflogReader("refs/heads/master").getLastEntry()
+ .getComment());
+ assertEquals("branch: change to a",
+ refDb.getReflogReader("refs/heads/a").getLastEntry()
+ .getComment());
+ assertEquals("branch: change to HEAD",
+ refDb.getReflogReader("HEAD").getLastEntry().getComment());
}
@Test
public void testReadLineWithMissingComment() throws Exception {
setupReflog("logs/refs/heads/master", oneLineWithoutComment);
- final ReflogReader reader = db.getReflogReader("master");
+ final ReflogReader reader = db.getRefDatabase()
+ .getReflogReader("refs/heads/master");
ReflogEntry e = reader.getLastEntry();
assertEquals(ObjectId
.fromString("da85355dfc525c9f6f3927b876f379f46ccf826e"), e
@@ -183,15 +188,18 @@ public class ReflogReaderTest extends SampleDataRepositoryTestCase {
@Test
public void testNoLog() throws Exception {
- assertEquals(0, db.getReflogReader("master").getReverseEntries().size());
- assertNull(db.getReflogReader("master").getLastEntry());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(0,
+ refDb.getReflogReader("refs/heads/master").getReverseEntries()
+ .size());
+ assertNull(refDb.getReflogReader("refs/heads/master").getLastEntry());
}
@Test
public void testCheckout() throws Exception {
setupReflog("logs/HEAD", switchBranch);
- List<ReflogEntry> entries = db.getReflogReader(Constants.HEAD)
- .getReverseEntries();
+ List<ReflogEntry> entries = db.getRefDatabase()
+ .getReflogReader(Constants.HEAD).getReverseEntries();
assertEquals(1, entries.size());
ReflogEntry entry = entries.get(0);
CheckoutEntry checkout = entry.parseCheckout();
@@ -238,7 +246,7 @@ public class ReflogReaderTest extends SampleDataRepositoryTestCase {
private void setupReflog(String logName, byte[] data)
throws FileNotFoundException, IOException {
- File logfile = new File(db.getDirectory(), logName);
+ File logfile = new File(db.getCommonDirectory(), logName);
if (!logfile.getParentFile().mkdirs()
&& !logfile.getParentFile().isDirectory()) {
throw new IOException(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java
index 8d0e99dea0..a8363336d9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java
@@ -16,6 +16,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
@@ -32,7 +34,7 @@ public class ReflogWriterTest extends SampleDataRepositoryTestCase {
ReflogWriter writer =
new ReflogWriter((RefDirectory) db.getRefDatabase());
PersonIdent ident = new PersonIdent("John Doe", "john@doe.com",
- 1243028200000L, 120);
+ Instant.ofEpochMilli(1243028200000L), ZoneOffset.ofHours(2));
ObjectId oldId = ObjectId
.fromString("da85355dfc525c9f6f3927b876f379f46ccf826e");
ObjectId newId = ObjectId
@@ -48,7 +50,7 @@ public class ReflogWriterTest extends SampleDataRepositoryTestCase {
private void readReflog(byte[] buffer)
throws FileNotFoundException, IOException {
- File logfile = new File(db.getDirectory(), "logs/refs/heads/master");
+ File logfile = new File(db.getCommonDirectory(), "logs/refs/heads/master");
if (!logfile.getParentFile().mkdirs()
&& !logfile.getParentFile().isDirectory()) {
throw new IOException(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
index 49e8a7be66..e067beb317 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
@@ -28,6 +28,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.time.Instant;
+import java.time.ZoneOffset;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -374,8 +375,10 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
public void test009_CreateCommitOldFormat() throws IOException {
final ObjectId treeId = insertTree(new TreeFormatter());
final CommitBuilder c = new CommitBuilder();
- c.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
- c.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
+ c.setAuthor(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
+ c.setCommitter(new PersonIdent(committer,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
c.setMessage("A Commit\n");
c.setTreeId(treeId);
assertEquals(treeId, c.getTreeId());
@@ -411,7 +414,8 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
final TagBuilder t = new TagBuilder();
t.setObjectId(emptyId, Constants.OBJ_BLOB);
t.setTag("test020");
- t.setTagger(new PersonIdent(author, 1154236443000L, -4 * 60));
+ t.setTagger(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
t.setMessage("test020 tagged\n");
ObjectId actid = insertTag(t);
assertEquals("6759556b09fbb4fd8ae5e315134481cc25d46954", actid.name());
@@ -419,8 +423,9 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
RevTag mapTag = parseTag(actid);
assertEquals(Constants.OBJ_BLOB, mapTag.getObject().getType());
assertEquals("test020 tagged\n", mapTag.getFullMessage());
- assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag
- .getTaggerIdent());
+ assertEquals(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)),
+ mapTag.getTaggerIdent());
assertEquals("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", mapTag
.getObject().getId().name());
}
@@ -434,7 +439,8 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
final TagBuilder t = new TagBuilder();
t.setObjectId(almostEmptyTreeId, Constants.OBJ_TREE);
t.setTag("test021");
- t.setTagger(new PersonIdent(author, 1154236443000L, -4 * 60));
+ t.setTagger(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
t.setMessage("test021 tagged\n");
ObjectId actid = insertTag(t);
assertEquals("b0517bc8dbe2096b419d42424cd7030733f4abe5", actid.name());
@@ -442,8 +448,9 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
RevTag mapTag = parseTag(actid);
assertEquals(Constants.OBJ_TREE, mapTag.getObject().getType());
assertEquals("test021 tagged\n", mapTag.getFullMessage());
- assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag
- .getTaggerIdent());
+ assertEquals(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)),
+ mapTag.getTaggerIdent());
assertEquals("417c01c8795a35b8e835113a85a5c0c1c77f67fb", mapTag
.getObject().getId().name());
}
@@ -455,17 +462,18 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
almostEmptyTree.append("empty", FileMode.REGULAR_FILE, emptyId);
final ObjectId almostEmptyTreeId = insertTree(almostEmptyTree);
final CommitBuilder almostEmptyCommit = new CommitBuilder();
- almostEmptyCommit.setAuthor(new PersonIdent(author, 1154236443000L,
- -2 * 60)); // not exactly the same
- almostEmptyCommit.setCommitter(new PersonIdent(author, 1154236443000L,
- -2 * 60));
+ almostEmptyCommit.setAuthor(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-2)));
+ almostEmptyCommit.setCommitter(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-2)));
almostEmptyCommit.setMessage("test022\n");
almostEmptyCommit.setTreeId(almostEmptyTreeId);
ObjectId almostEmptyCommitId = insertCommit(almostEmptyCommit);
final TagBuilder t = new TagBuilder();
t.setObjectId(almostEmptyCommitId, Constants.OBJ_COMMIT);
t.setTag("test022");
- t.setTagger(new PersonIdent(author, 1154236443000L, -4 * 60));
+ t.setTagger(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
t.setMessage("test022 tagged\n");
ObjectId actid = insertTag(t);
assertEquals("0ce2ebdb36076ef0b38adbe077a07d43b43e3807", actid.name());
@@ -473,8 +481,9 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
RevTag mapTag = parseTag(actid);
assertEquals(Constants.OBJ_COMMIT, mapTag.getObject().getType());
assertEquals("test022 tagged\n", mapTag.getFullMessage());
- assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag
- .getTaggerIdent());
+ assertEquals(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)),
+ mapTag.getTaggerIdent());
assertEquals("b5d3b45a96b340441f5abb9080411705c51cc86c", mapTag
.getObject().getId().name());
}
@@ -488,9 +497,9 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
CommitBuilder commit = new CommitBuilder();
commit.setTreeId(almostEmptyTreeId);
commit.setAuthor(new PersonIdent("Joe H\u00e4cker", "joe@example.com",
- 4294967295000L, 60));
+ Instant.ofEpochMilli(4294967295000L), ZoneOffset.ofHours(1)));
commit.setCommitter(new PersonIdent("Joe Hacker", "joe2@example.com",
- 4294967295000L, 60));
+ Instant.ofEpochMilli(4294967295000L), ZoneOffset.ofHours(1)));
commit.setEncoding(UTF_8);
commit.setMessage("\u00dcbergeeks");
ObjectId cid = insertCommit(commit);
@@ -509,9 +518,9 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
CommitBuilder commit = new CommitBuilder();
commit.setTreeId(almostEmptyTreeId);
commit.setAuthor(new PersonIdent("Joe H\u00e4cker", "joe@example.com",
- 4294967295000L, 60));
+ Instant.ofEpochMilli(4294967295000L), ZoneOffset.ofHours(1)));
commit.setCommitter(new PersonIdent("Joe Hacker", "joe2@example.com",
- 4294967295000L, 60));
+ Instant.ofEpochMilli(4294967295000L), ZoneOffset.ofHours(1)));
commit.setEncoding(ISO_8859_1);
commit.setMessage("\u00dcbergeeks");
ObjectId cid = insertCommit(commit);
@@ -544,8 +553,10 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
.fromString("00b1f73724f493096d1ffa0b0f1f1482dbb8c936"), treeId);
final CommitBuilder c1 = new CommitBuilder();
- c1.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
- c1.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
+ c1.setAuthor(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
+ c1.setCommitter(new PersonIdent(committer,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
c1.setMessage("A Commit\n");
c1.setTreeId(treeId);
assertEquals(treeId, c1.getTreeId());
@@ -555,8 +566,10 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
assertEquals(cmtid1, actid1);
final CommitBuilder c2 = new CommitBuilder();
- c2.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
- c2.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
+ c2.setAuthor(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
+ c2.setCommitter(new PersonIdent(committer,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
c2.setMessage("A Commit 2\n");
c2.setTreeId(treeId);
assertEquals(treeId, c2.getTreeId());
@@ -577,8 +590,10 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
assertEquals(actid1, rm2.getParent(0));
final CommitBuilder c3 = new CommitBuilder();
- c3.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
- c3.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
+ c3.setAuthor(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
+ c3.setCommitter(new PersonIdent(committer,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
c3.setMessage("A Commit 3\n");
c3.setTreeId(treeId);
assertEquals(treeId, c3.getTreeId());
@@ -600,8 +615,10 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
assertEquals(actid2, rm3.getParent(1));
final CommitBuilder c4 = new CommitBuilder();
- c4.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
- c4.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
+ c4.setAuthor(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
+ c4.setCommitter(new PersonIdent(committer,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
c4.setMessage("A Commit 4\n");
c4.setTreeId(treeId);
assertEquals(treeId, c3.getTreeId());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java
new file mode 100644
index 0000000000..82f3eb1e08
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * 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.midx;
+
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.CHUNK_LOOKUP_WIDTH;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_LARGEOFFSETS;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OBJECTOFFSETS;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OIDFANOUT;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OIDLOOKUP;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_PACKNAMES;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_REVINDEX;
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.junit.FakeIndexFactory;
+import org.eclipse.jgit.junit.FakeIndexFactory.IndexObject;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.util.NB;
+import org.junit.Test;
+
+public class MultiPackIndexWriterTest {
+
+ @Test
+ public void write_allSmallOffsets() throws IOException {
+ PackIndex index1 = indexOf(
+ object("0000000000000000000000000000000000000001", 500),
+ object("0000000000000000000000000000000000000003", 1500),
+ object("0000000000000000000000000000000000000005", 3000));
+ PackIndex index2 = indexOf(
+ object("0000000000000000000000000000000000000002", 500),
+ object("0000000000000000000000000000000000000004", 1500),
+ object("0000000000000000000000000000000000000006", 3000));
+
+ Map<String, PackIndex> data = Map.of("packname1", index1, "packname2",
+ index2);
+
+ MultiPackIndexWriter writer = new MultiPackIndexWriter();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
+ // header (12 bytes)
+ // + chunkHeader (6 * 12 bytes)
+ // + fanout table (256 * 4 bytes)
+ // + OIDs (6 * 20 bytes)
+ // + (pack, offset) pairs (6 * 8)
+ // + RIDX (6 * 4 bytes)
+ // + packfile names (2 * 10)
+ // + checksum (20)
+ assertEquals(1340, out.size());
+ List<Integer> chunkIds = readChunkIds(out);
+ assertEquals(5, chunkIds.size());
+ assertEquals(0, chunkIds.indexOf(MIDX_CHUNKID_OIDFANOUT));
+ assertEquals(1, chunkIds.indexOf(MIDX_CHUNKID_OIDLOOKUP));
+ assertEquals(2, chunkIds.indexOf(MIDX_CHUNKID_OBJECTOFFSETS));
+ assertEquals(3, chunkIds.indexOf(MIDX_CHUNKID_REVINDEX));
+ assertEquals(4, chunkIds.indexOf(MIDX_CHUNKID_PACKNAMES));
+ }
+
+ @Test
+ public void write_smallOffset_limit() throws IOException {
+ PackIndex index1 = indexOf(
+ object("0000000000000000000000000000000000000001", 500),
+ object("0000000000000000000000000000000000000003", 1500),
+ object("0000000000000000000000000000000000000005", (1L << 32) -1));
+ PackIndex index2 = indexOf(
+ object("0000000000000000000000000000000000000002", 500),
+ object("0000000000000000000000000000000000000004", 1500),
+ object("0000000000000000000000000000000000000006", 3000));
+ Map<String, PackIndex> data =
+ Map.of("packname1", index1, "packname2", index2);
+
+ MultiPackIndexWriter writer = new MultiPackIndexWriter();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
+ // header (12 bytes)
+ // + chunkHeader (6 * 12 bytes)
+ // + fanout table (256 * 4 bytes)
+ // + OIDs (6 * 20 bytes)
+ // + (pack, offset) pairs (6 * 8)
+ // + RIDX (6 * 4 bytes)
+ // + packfile names (2 * 10)
+ // + checksum (20)
+ assertEquals(1340, out.size());
+ List<Integer> chunkIds = readChunkIds(out);
+ assertEquals(5, chunkIds.size());
+ assertEquals(0, chunkIds.indexOf(MIDX_CHUNKID_OIDFANOUT));
+ assertEquals(1, chunkIds.indexOf(MIDX_CHUNKID_OIDLOOKUP));
+ assertEquals(2, chunkIds.indexOf(MIDX_CHUNKID_OBJECTOFFSETS));
+ assertEquals(3, chunkIds.indexOf(MIDX_CHUNKID_REVINDEX));
+ assertEquals(4, chunkIds.indexOf(MIDX_CHUNKID_PACKNAMES));
+ }
+
+ @Test
+ public void write_largeOffset() throws IOException {
+ PackIndex index1 = indexOf(
+ object("0000000000000000000000000000000000000001", 500),
+ object("0000000000000000000000000000000000000003", 1500),
+ object("0000000000000000000000000000000000000005", 1L << 32));
+ PackIndex index2 = indexOf(
+ object("0000000000000000000000000000000000000002", 500),
+ object("0000000000000000000000000000000000000004", 1500),
+ object("0000000000000000000000000000000000000006", 3000));
+ Map<String, PackIndex> data =
+ Map.of("packname1", index1, "packname2", index2);
+
+ MultiPackIndexWriter writer = new MultiPackIndexWriter();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
+ // header (12 bytes)
+ // + chunkHeader (7 * 12 bytes)
+ // + fanout table (256 * 4 bytes)
+ // + OIDs (6 * 20 bytes)
+ // + (pack, offset) pairs (6 * 8)
+ // + (large-offset) (1 * 8)
+ // + RIDX (6 * 4 bytes)
+ // + packfile names (2 * 10)
+ // + checksum (20)
+ assertEquals(1360, out.size());
+ List<Integer> chunkIds = readChunkIds(out);
+ assertEquals(6, chunkIds.size());
+ assertEquals(0, chunkIds.indexOf(MIDX_CHUNKID_OIDFANOUT));
+ assertEquals(1, chunkIds.indexOf(MIDX_CHUNKID_OIDLOOKUP));
+ assertEquals(2, chunkIds.indexOf(MIDX_CHUNKID_OBJECTOFFSETS));
+ assertEquals(3, chunkIds.indexOf(MIDX_CHUNKID_LARGEOFFSETS));
+ assertEquals(4, chunkIds.indexOf(MIDX_CHUNKID_REVINDEX));
+ assertEquals(5, chunkIds.indexOf(MIDX_CHUNKID_PACKNAMES));
+ }
+
+ private List<Integer> readChunkIds(ByteArrayOutputStream out) {
+ List<Integer> chunkIds = new ArrayList<>();
+ byte[] raw = out.toByteArray();
+ int numChunks = raw[6];
+ int position = 12;
+ for (int i = 0; i < numChunks; i++) {
+ chunkIds.add(NB.decodeInt32(raw, position));
+ position += CHUNK_LOOKUP_WIDTH;
+ }
+ return chunkIds;
+ }
+
+ private static PackIndex indexOf(IndexObject... objs) {
+ return FakeIndexFactory.indexOf(Arrays.asList(objs));
+ }
+
+ private static IndexObject object(String name, long offset) {
+ return new IndexObject(name, offset);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java
new file mode 100644
index 0000000000..1d8bde0f44
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * 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.midx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.junit.FakeIndexFactory;
+import org.eclipse.jgit.junit.FakeIndexFactory.IndexObject;
+import org.junit.Test;
+
+public class PackIndexMergerTest {
+
+ @Test
+ public void rawIterator_noDuplicates() {
+ PackIndex idxOne = indexOf(
+ oidOffset("0000000000000000000000000000000000000001", 500),
+ oidOffset("0000000000000000000000000000000000000005", 12),
+ oidOffset("0000000000000000000000000000000000000010", 1500));
+ PackIndex idxTwo = indexOf(
+ oidOffset("0000000000000000000000000000000000000002", 501),
+ oidOffset("0000000000000000000000000000000000000003", 13),
+ oidOffset("0000000000000000000000000000000000000015", 1501));
+ PackIndex idxThree = indexOf(
+ oidOffset("0000000000000000000000000000000000000004", 502),
+ oidOffset("0000000000000000000000000000000000000007", 14),
+ oidOffset("0000000000000000000000000000000000000012", 1502));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+ assertEquals(9, merger.getUniqueObjectCount());
+ assertEquals(3, merger.getPackCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ Iterator<PackIndexMerger.MidxMutableEntry> it = merger.rawIterator();
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 0, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000002", 1, 501);
+ assertNextEntry(it, "0000000000000000000000000000000000000003", 1, 13);
+ assertNextEntry(it, "0000000000000000000000000000000000000004", 2, 502);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 0, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000007", 2, 14);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
+ 1500);
+ assertNextEntry(it, "0000000000000000000000000000000000000012", 2,
+ 1502);
+ assertNextEntry(it, "0000000000000000000000000000000000000015", 1,
+ 1501);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void rawIterator_allDuplicates() {
+ PackIndex idxOne = indexOf(
+ oidOffset("0000000000000000000000000000000000000001", 500),
+ oidOffset("0000000000000000000000000000000000000005", 12),
+ oidOffset("0000000000000000000000000000000000000010", 1500));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idxOne, "p2", idxOne, "p3", idxOne));
+ assertEquals(3, merger.getUniqueObjectCount());
+ assertEquals(3, merger.getPackCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ Iterator<PackIndexMerger.MidxMutableEntry> it = merger.rawIterator();
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 0, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 1, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 2, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 0, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 1, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 2, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
+ 1500);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 1,
+ 1500);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 2,
+ 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void bySha1Iterator_noDuplicates() {
+ PackIndex idxOne = indexOf(
+ oidOffset("0000000000000000000000000000000000000001", 500),
+ oidOffset("0000000000000000000000000000000000000005", 12),
+ oidOffset("0000000000000000000000000000000000000010", 1500));
+ PackIndex idxTwo = indexOf(
+ oidOffset("0000000000000000000000000000000000000002", 501),
+ oidOffset("0000000000000000000000000000000000000003", 13),
+ oidOffset("0000000000000000000000000000000000000015", 1501));
+ PackIndex idxThree = indexOf(
+ oidOffset("0000000000000000000000000000000000000004", 502),
+ oidOffset("0000000000000000000000000000000000000007", 14),
+ oidOffset("0000000000000000000000000000000000000012", 1502));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+ assertEquals(9, merger.getUniqueObjectCount());
+ assertEquals(3, merger.getPackCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ Iterator<PackIndexMerger.MidxMutableEntry> it = merger.bySha1Iterator();
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 0, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000002", 1, 501);
+ assertNextEntry(it, "0000000000000000000000000000000000000003", 1, 13);
+ assertNextEntry(it, "0000000000000000000000000000000000000004", 2, 502);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 0, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000007", 2, 14);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
+ 1500);
+ assertNextEntry(it, "0000000000000000000000000000000000000012", 2,
+ 1502);
+ assertNextEntry(it, "0000000000000000000000000000000000000015", 1,
+ 1501);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void bySha1Iterator_allDuplicates() {
+ PackIndex idxOne = indexOf(
+ oidOffset("0000000000000000000000000000000000000001", 500),
+ oidOffset("0000000000000000000000000000000000000005", 12),
+ oidOffset("0000000000000000000000000000000000000010", 1500));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idxOne, "p2", idxOne, "p3", idxOne));
+ assertEquals(3, merger.getUniqueObjectCount());
+ assertEquals(3, merger.getPackCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ Iterator<PackIndexMerger.MidxMutableEntry> it = merger.bySha1Iterator();
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 0, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 0, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
+ 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void bySha1Iterator_differentIndexSizes() {
+ PackIndex idxOne = indexOf(
+ oidOffset("0000000000000000000000000000000000000010", 1500));
+ PackIndex idxTwo = indexOf(
+ oidOffset("0000000000000000000000000000000000000002", 500),
+ oidOffset("0000000000000000000000000000000000000003", 12));
+ PackIndex idxThree = indexOf(
+ oidOffset("0000000000000000000000000000000000000004", 500),
+ oidOffset("0000000000000000000000000000000000000007", 12),
+ oidOffset("0000000000000000000000000000000000000012", 1500));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+ assertEquals(6, merger.getUniqueObjectCount());
+ assertEquals(3, merger.getPackCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ Iterator<PackIndexMerger.MidxMutableEntry> it = merger.bySha1Iterator();
+ assertNextEntry(it, "0000000000000000000000000000000000000002", 1, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000003", 1, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000004", 2, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000007", 2, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
+ 1500);
+ assertNextEntry(it, "0000000000000000000000000000000000000012", 2,
+ 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void merger_noIndexes() {
+ PackIndexMerger merger = new PackIndexMerger(Map.of());
+ assertEquals(0, merger.getUniqueObjectCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ assertTrue(merger.getPackNames().isEmpty());
+ assertEquals(0, merger.getPackCount());
+ assertFalse(merger.bySha1Iterator().hasNext());
+ }
+
+ @Test
+ public void merger_emptyIndexes() {
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", indexOf(), "p2", indexOf()));
+ assertEquals(0, merger.getUniqueObjectCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ assertEquals(2, merger.getPackNames().size());
+ assertEquals(2, merger.getPackCount());
+ assertFalse(merger.bySha1Iterator().hasNext());
+ }
+
+ @Test
+ public void bySha1Iterator_largeOffsets_needsChunk() {
+ PackIndex idx1 = indexOf(
+ oidOffset("0000000000000000000000000000000000000002", 1L << 32),
+ oidOffset("0000000000000000000000000000000000000004", 12));
+ PackIndex idx2 = indexOf(oidOffset(
+ "0000000000000000000000000000000000000003", (1L << 31) + 10));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idx1, "p2", idx2));
+ assertTrue(merger.needsLargeOffsetsChunk());
+ assertEquals(2, merger.getOffsetsOver31BitsCount());
+ assertEquals(3, merger.getUniqueObjectCount());
+ }
+
+ @Test
+ public void bySha1Iterator_largeOffsets_noChunk() {
+ // If no value is over 2^32-1, then we don't need large offset
+ PackIndex idx1 = indexOf(
+ oidOffset("0000000000000000000000000000000000000002",
+ (1L << 31) + 15),
+ oidOffset("0000000000000000000000000000000000000004", 12));
+ PackIndex idx2 = indexOf(oidOffset(
+ "0000000000000000000000000000000000000003", (1L << 31) + 10));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idx1, "p2", idx2));
+ assertFalse(merger.needsLargeOffsetsChunk());
+ assertEquals(2, merger.getOffsetsOver31BitsCount());
+ assertEquals(3, merger.getUniqueObjectCount());
+ }
+
+ private static void assertNextEntry(
+ Iterator<PackIndexMerger.MidxMutableEntry> it, String oid,
+ int packId, long offset) {
+ assertTrue(it.hasNext());
+ PackIndexMerger.MidxMutableEntry e = it.next();
+ assertEquals(oid, e.getObjectId().name());
+ assertEquals(packId, e.getPackId());
+ assertEquals(offset, e.getOffset());
+ }
+
+ private static IndexObject oidOffset(String oid, long offset) {
+ return new IndexObject(oid, offset);
+ }
+
+ private static PackIndex indexOf(IndexObject... objs) {
+ return FakeIndexFactory.indexOf(Arrays.asList(objs));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexPeekIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexPeekIteratorTest.java
new file mode 100644
index 0000000000..917288a899
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexPeekIteratorTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * 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.midx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.Arrays;
+
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.junit.FakeIndexFactory;
+import org.junit.Test;
+
+public class PackIndexPeekIteratorTest {
+ @Test
+ public void next() {
+ PackIndex index1 = indexOf(
+ object("0000000000000000000000000000000000000001", 500),
+ object("0000000000000000000000000000000000000003", 1500),
+ object("0000000000000000000000000000000000000005", 3000));
+ PackIndexMerger.PackIndexPeekIterator it = new PackIndexMerger.PackIndexPeekIterator(0, index1);
+ assertEquals("0000000000000000000000000000000000000001", it.next().name());
+ assertEquals("0000000000000000000000000000000000000003", it.next().name());
+ assertEquals("0000000000000000000000000000000000000005", it.next().name());
+ assertNull(it.next());
+ }
+
+ @Test
+ public void peek_doesNotAdvance() {
+ PackIndex index1 = indexOf(
+ object("0000000000000000000000000000000000000001", 500),
+ object("0000000000000000000000000000000000000003", 1500),
+ object("0000000000000000000000000000000000000005", 3000));
+ PackIndexMerger.PackIndexPeekIterator it = new PackIndexMerger.PackIndexPeekIterator(0, index1);
+ it.next();
+ assertEquals("0000000000000000000000000000000000000001", it.peek().name());
+ assertEquals("0000000000000000000000000000000000000001", it.peek().name());
+ it.next();
+ assertEquals("0000000000000000000000000000000000000003", it.peek().name());
+ assertEquals("0000000000000000000000000000000000000003", it.peek().name());
+ it.next();
+ assertEquals("0000000000000000000000000000000000000005", it.peek().name());
+ assertEquals("0000000000000000000000000000000000000005", it.peek().name());
+ it.next();
+ assertNull(it.peek());
+ assertNull(it.peek());
+ }
+
+ @Test
+ public void empty() {
+ PackIndex index1 = indexOf();
+ PackIndexMerger.PackIndexPeekIterator it = new PackIndexMerger.PackIndexPeekIterator(0, index1);
+ assertNull(it.next());
+ assertNull(it.peek());
+ }
+
+ private static PackIndex indexOf(FakeIndexFactory.IndexObject... objs) {
+ return FakeIndexFactory.indexOf(Arrays.asList(objs));
+ }
+
+ private static FakeIndexFactory.IndexObject object(String name, long offset) {
+ return new FakeIndexFactory.IndexObject(name, offset);
+ }
+} \ No newline at end of file
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 ea0d92acfd..a54002bc74 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
@@ -29,6 +29,8 @@ import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -175,7 +177,8 @@ public class ReftableTest {
@Test
public void hasObjLogs() throws IOException {
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
String msg = "test";
ReftableConfig cfg = new ReftableConfig();
cfg.setIndexObjects(false);
@@ -617,7 +620,8 @@ public class ReftableTest {
.setMinUpdateIndex(1)
.setMaxUpdateIndex(2)
.begin();
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
String msg = "test";
writer.writeLog(MASTER, 1, who, ObjectId.zeroId(), id(1), msg);
@@ -633,7 +637,8 @@ public class ReftableTest {
.setMinUpdateIndex(1)
.setMaxUpdateIndex(1)
.begin();
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
String msg = "test";
writer.writeLog(NEXT, 1, who, ObjectId.zeroId(), id(1), msg);
@@ -647,7 +652,8 @@ public class ReftableTest {
public void withReflog() throws IOException {
Ref master = ref(MASTER, 1);
Ref next = ref(NEXT, 2);
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
String msg = "test";
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@@ -712,11 +718,14 @@ public class ReftableTest {
writer.writeRef(master);
writer.writeRef(next);
- PersonIdent who1 = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who1 = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
writer.writeLog(MASTER, 3, who1, ObjectId.zeroId(), id(1), "1");
- PersonIdent who2 = new PersonIdent("Log", "Ger", 1500079710, -8 * 60);
+ PersonIdent who2 = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
writer.writeLog(MASTER, 2, who2, id(1), id(2), "2");
- PersonIdent who3 = new PersonIdent("Log", "Ger", 1500079711, -8 * 60);
+ PersonIdent who3 = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
writer.writeLog(MASTER, 1, who3, id(2), id(3), "3");
writer.finish();
@@ -753,7 +762,8 @@ public class ReftableTest {
.setMaxUpdateIndex(1)
.setConfig(cfg)
.begin();
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
// Fill out the 1st ref block.
List<String> names = new ArrayList<>();
@@ -782,7 +792,8 @@ public class ReftableTest {
@Test
public void reflogSeek() throws IOException {
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochSecond(1500079709), ZoneOffset.ofHours(-8));
String msg = "test";
String msgNext = "test next";
@@ -827,7 +838,8 @@ public class ReftableTest {
@Test
public void reflogSeekPrefix() throws IOException {
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ReftableWriter writer = new ReftableWriter(buffer)
@@ -850,7 +862,8 @@ public class ReftableTest {
@Test
public void onlyReflog() throws IOException {
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
String msg = "test";
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@@ -916,7 +929,8 @@ public class ReftableTest {
writer.writeRef(ref);
}
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
for (Ref ref : refs) {
writer.writeLog(ref.getName(), 1, who,
ObjectId.zeroId(), ref.getObjectId(),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java
index 450b753d94..1581d49797 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java
@@ -11,6 +11,7 @@
package org.eclipse.jgit.junit;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.time.Instant.EPOCH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -18,7 +19,6 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import java.util.Date;
import java.util.regex.Pattern;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
@@ -199,8 +199,8 @@ public class TestRepositoryTest {
assertEquals(orig.getAuthorIdent(), amended.getAuthorIdent());
// Committer name/email is the same, but time was incremented.
- assertEquals(new PersonIdent(orig.getCommitterIdent(), new Date(0)),
- new PersonIdent(amended.getCommitterIdent(), new Date(0)));
+ assertEquals(new PersonIdent(orig.getCommitterIdent(), EPOCH),
+ new PersonIdent(amended.getCommitterIdent(), EPOCH));
assertTrue(orig.getCommitTime() < amended.getCommitTime());
assertEquals("foo contents", blobAsString(amended, "foo"));
@@ -275,9 +275,9 @@ public class TestRepositoryTest {
RevCommit toPick = tr.commit()
.parent(tr.commit().create()) // Can't cherry-pick root.
.author(new PersonIdent("Cherrypick Author", "cpa@example.com",
- tr.getDate(), tr.getTimeZone()))
+ tr.getInstant(), tr.getTimeZoneId()))
.author(new PersonIdent("Cherrypick Committer", "cpc@example.com",
- tr.getDate(), tr.getTimeZone()))
+ tr.getInstant(), tr.getTimeZoneId()))
.message("message to cherry-pick")
.add("bar", "bar contents\n")
.create();
@@ -294,8 +294,8 @@ public class TestRepositoryTest {
assertEquals(toPick.getAuthorIdent(), result.getAuthorIdent());
// Committer name/email matches default, and time was incremented.
- assertEquals(new PersonIdent(head.getCommitterIdent(), new Date(0)),
- new PersonIdent(result.getCommitterIdent(), new Date(0)));
+ assertEquals(new PersonIdent(head.getCommitterIdent(), EPOCH),
+ new PersonIdent(result.getCommitterIdent(), EPOCH));
assertTrue(toPick.getCommitTime() < result.getCommitTime());
assertEquals("message to cherry-pick", result.getFullMessage());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
index 31940a16f7..06fee8ea71 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
@@ -1636,6 +1636,47 @@ public class ConfigTest {
assertFalse(config.get(CoreConfig.KEY).enableCommitGraph());
}
+ @Test
+ public void testGetNoDefaultBoolean() {
+ Config config = new Config();
+ assertNull(config.getBoolean("foo", "bar"));
+ assertNull(config.getBoolean("foo", "bar", "baz"));
+ }
+
+ @Test
+ public void testGetNoDefaultEnum() {
+ Config config = new Config();
+ assertNull(config.getEnum(new TestEnum[] { TestEnum.ONE_TWO }, "foo",
+ "bar", "baz"));
+ }
+
+ @Test
+ public void testGetNoDefaultInt() {
+ Config config = new Config();
+ assertNull(config.getInt("foo", "bar"));
+ assertNull(config.getInt("foo", "bar", "baz"));
+ }
+ @Test
+ public void testGetNoDefaultIntInRange() {
+ Config config = new Config();
+ assertNull(config.getIntInRange("foo", "bar", 1, 5));
+ assertNull(config.getIntInRange("foo", "bar", "baz", 1, 5));
+ }
+
+ @Test
+ public void testGetNoDefaultLong() {
+ Config config = new Config();
+ assertNull(config.getLong("foo", "bar"));
+ assertNull(config.getLong("foo", "bar", "baz"));
+ }
+
+ @Test
+ public void testGetNoDefaultTimeUnit() {
+ Config config = new Config();
+ assertNull(config.getTimeUnit("foo", "bar", "baz",
+ TimeUnit.SECONDS));
+ }
+
private static void assertValueRoundTrip(String value)
throws ConfigInvalidException {
assertValueRoundTrip(value, value);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java
index 32f6766d47..5c2b190777 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java
@@ -96,6 +96,16 @@ public class GpgConfigTest {
}
@Test
+ public void testGetKeyFormat_ssh() throws Exception {
+ Config c = parse("" //
+ + "[gpg]\n" //
+ + " format = ssh\n" //
+ );
+
+ assertEquals(GpgConfig.GpgFormat.SSH, new GpgConfig(c).getKeyFormat());
+ }
+
+ @Test
public void testGetSigningKey() throws Exception {
Config c = parse("" //
+ "[user]\n" //
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
index 2b7b6ca76c..cd98606e53 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
@@ -2,7 +2,7 @@
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
- * Copyright (C) 2013, Robin Stocker <robin@nibor.org> and others
+ * Copyright (C) 2013, 2025 Robin Stocker <robin@nibor.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
@@ -539,7 +539,7 @@ public class IndexDiffTest extends RepositoryTestCase {
assertTrue(diff.getAssumeUnchanged().contains("file3"));
assertTrue(diff.getModified().contains("file"));
- git.add().addFilepattern(".").call();
+ git.add().addFilepattern(".").setAll(false).call();
iterator = new FileTreeIterator(db);
diff = new IndexDiff(db, Constants.HEAD, iterator);
@@ -551,6 +551,18 @@ public class IndexDiffTest extends RepositoryTestCase {
assertTrue(diff.getAssumeUnchanged().contains("file3"));
assertTrue(diff.getChanged().contains("file"));
assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
+
+ git.add().addFilepattern(".").call();
+
+ iterator = new FileTreeIterator(db);
+ diff = new IndexDiff(db, Constants.HEAD, iterator);
+ diff.diff();
+ assertEquals(1, diff.getAssumeUnchanged().size());
+ assertEquals(0, diff.getModified().size());
+ assertEquals(1, diff.getChanged().size());
+ assertTrue(diff.getAssumeUnchanged().contains("file2"));
+ assertTrue(diff.getChanged().contains("file"));
+ assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
index 21032c341f..d6f0b038d2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
@@ -16,6 +16,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import java.nio.ByteBuffer;
import java.util.Locale;
import org.eclipse.jgit.errors.InvalidObjectIdException;
@@ -153,4 +154,16 @@ public class ObjectIdTest {
assertEquals(ObjectId.fromRaw(exp).name(), id.name());
}
}
+
+ @Test
+ public void test_toFromByteBuffer_raw() {
+ ObjectId oid = ObjectId
+ .fromString("ff00eedd003713bb1bb26b808ec9312548e73946");
+ ByteBuffer anObject = ByteBuffer.allocate(Constants.OBJECT_ID_LENGTH);
+ oid.copyRawTo(anObject);
+ anObject.flip();
+
+ ObjectId actual = ObjectId.fromRaw(anObject);
+ assertEquals(oid.name(), actual.name());
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PersonIdentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PersonIdentTest.java
index 97da1757e0..943a68b82c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PersonIdentTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PersonIdentTest.java
@@ -55,7 +55,8 @@ public class PersonIdentTest {
p.getWhenAsInstant());
assertEquals("A U Thor <author@example.com> 1142878501 -0500",
p.toExternalString());
- assertEquals(ZoneId.of("GMT-05:00"), p.getZoneId());
+ assertEquals(ZoneId.of("GMT-05:00").getRules().getOffset(
+ Instant.ofEpochMilli(1142878501000L)), p.getZoneOffset());
}
@Test
@@ -69,7 +70,8 @@ public class PersonIdentTest {
p.getWhenAsInstant());
assertEquals("A U Thor <author@example.com> 1142878501 +0530",
p.toExternalString());
- assertEquals(ZoneId.of("GMT+05:30"), p.getZoneId());
+ assertEquals(ZoneId.of("GMT+05:30").getRules().getOffset(
+ Instant.ofEpochMilli(1142878501000L)), p.getZoneOffset());
}
@SuppressWarnings("unused")
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java
index b02f245865..85f9612b6a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java
@@ -71,6 +71,11 @@ public class RefDatabaseConflictingNamesTest {
}
@Override
+ public ReflogReader getReflogReader(Ref ref) throws IOException {
+ return null;
+ }
+
+ @Override
public void create() throws IOException {
// Not needed
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
index 854180e3ea..a93937eeea 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
@@ -16,6 +16,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZoneOffset;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.storage.file.FileBasedConfig;
@@ -24,22 +27,23 @@ import org.junit.Test;
public class ReflogConfigTest extends RepositoryTestCase {
@Test
public void testlogAllRefUpdates() throws Exception {
- long commitTime = 1154236443000L;
- int tz = -4 * 60;
+ Instant commitTime = Instant.ofEpochSecond(1154236443L);
+ ZoneOffset tz = ZoneOffset.ofHours(-4);
// check that there are no entries in the reflog and turn off writing
// reflogs
- assertTrue(db.getReflogReader(Constants.HEAD).getReverseEntries()
+ RefDatabase refDb = db.getRefDatabase();
+ assertTrue(refDb.getReflogReader(Constants.HEAD).getReverseEntries()
.isEmpty());
- final FileBasedConfig cfg = db.getConfig();
+ FileBasedConfig cfg = db.getConfig();
cfg.setBoolean("core", null, "logallrefupdates", false);
cfg.save();
// do one commit and check that reflog size is 0: no reflogs should be
// written
commit("A Commit\n", commitTime, tz);
- commitTime += 60 * 1000;
- assertTrue("Reflog for HEAD still contain no entry", db
+ commitTime = commitTime.plus(Duration.ofMinutes(1));
+ assertTrue("Reflog for HEAD still contain no entry", refDb
.getReflogReader(Constants.HEAD).getReverseEntries().isEmpty());
// set the logAllRefUpdates parameter to true and check it
@@ -52,10 +56,10 @@ public class ReflogConfigTest extends RepositoryTestCase {
// do one commit and check that reflog size is increased to 1
commit("A Commit\n", commitTime, tz);
- commitTime += 60 * 1000;
- assertTrue(
- "Reflog for HEAD should contain one entry",
- db.getReflogReader(Constants.HEAD).getReverseEntries().size() == 1);
+ commitTime = commitTime.plus(Duration.ofMinutes(1));
+ assertTrue("Reflog for HEAD should contain one entry",
+ refDb.getReflogReader(Constants.HEAD).getReverseEntries()
+ .size() == 1);
// set the logAllRefUpdates parameter to false and check it
cfg.setBoolean("core", null, "logallrefupdates", false);
@@ -67,10 +71,10 @@ public class ReflogConfigTest extends RepositoryTestCase {
// do one commit and check that reflog size is 2
commit("A Commit\n", commitTime, tz);
- commitTime += 60 * 1000;
- assertTrue(
- "Reflog for HEAD should contain two entries",
- db.getReflogReader(Constants.HEAD).getReverseEntries().size() == 2);
+ commitTime = commitTime.plus(Duration.ofMinutes(1));
+ assertTrue("Reflog for HEAD should contain two entries",
+ refDb.getReflogReader(Constants.HEAD).getReverseEntries()
+ .size() == 2);
// set the logAllRefUpdates parameter to false and check it
cfg.setEnum("core", null, "logallrefupdates",
@@ -84,13 +88,13 @@ public class ReflogConfigTest extends RepositoryTestCase {
// do one commit and check that reflog size is 3
commit("A Commit\n", commitTime, tz);
assertTrue("Reflog for HEAD should contain three entries",
- db.getReflogReader(Constants.HEAD).getReverseEntries()
+ refDb.getReflogReader(Constants.HEAD).getReverseEntries()
.size() == 3);
}
- private void commit(String commitMsg, long commitTime, int tz)
+ private void commit(String commitMsg, Instant commitTime, ZoneOffset tz)
throws IOException {
- final CommitBuilder commit = new CommitBuilder();
+ CommitBuilder commit = new CommitBuilder();
commit.setAuthor(new PersonIdent(author, commitTime, tz));
commit.setCommitter(new PersonIdent(committer, commitTime, tz));
commit.setMessage(commitMsg);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
index ae811f830f..8865ba9ebd 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
@@ -15,6 +15,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import java.time.Instant;
+import java.time.ZoneOffset;
+
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.junit.RepositoryTestCase;
@@ -162,7 +165,8 @@ public class CherryPickTest extends RepositoryTestCase {
final ObjectId[] parentIds) throws Exception {
final CommitBuilder c = new CommitBuilder();
c.setTreeId(treeB.writeTree(odi));
- c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
+ c.setAuthor(new PersonIdent("A U Thor", "a.u.thor",
+ Instant.ofEpochSecond(1), ZoneOffset.UTC));
c.setCommitter(c.getAuthor());
c.setParentIds(parentIds);
c.setMessage("Tree " + c.getTreeId().name());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/GitlinkMergeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/GitlinkMergeTest.java
index f410960bec..b1998f30f8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/GitlinkMergeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/GitlinkMergeTest.java
@@ -15,6 +15,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.dircache.DirCache;
@@ -357,7 +359,8 @@ public class GitlinkMergeTest extends SampleDataRepositoryTestCase {
ObjectId[] parentIds) throws Exception {
CommitBuilder c = new CommitBuilder();
c.setTreeId(treeB.writeTree(odi));
- c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
+ c.setAuthor(new PersonIdent("A U Thor", "a.u.thor",
+ Instant.ofEpochSecond(1), ZoneOffset.UTC));
c.setCommitter(c.getAuthor());
c.setParentIds(parentIds);
c.setMessage("Tree " + c.getTreeId().name());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmUnionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmUnionTest.java
new file mode 100644
index 0000000000..3a8af7a00e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmUnionTest.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2024 Qualcomm Innovation Center, Inc.
+ *
+ * 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.merge;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.eclipse.jgit.diff.RawText;
+import org.eclipse.jgit.diff.RawTextComparator;
+import org.eclipse.jgit.lib.Constants;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.runner.RunWith;
+
+@RunWith(Theories.class)
+public class MergeAlgorithmUnionTest {
+ MergeFormatter fmt = new MergeFormatter();
+
+ private final boolean newlineAtEnd;
+
+ @DataPoints
+ public static boolean[] newlineAtEndDataPoints = { false, true };
+
+ public MergeAlgorithmUnionTest(boolean newlineAtEnd) {
+ this.newlineAtEnd = newlineAtEnd;
+ }
+
+ /**
+ * Check for a conflict where the second text was changed similar to the
+ * first one, but the second texts modification covers one more line.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testTwoConflictingModifications() throws IOException {
+ assertEquals(t("abZZdefghij"),
+ merge("abcdefghij", "abZdefghij", "aZZdefghij"));
+ }
+
+ /**
+ * Test a case where we have three consecutive chunks. The first text
+ * modifies all three chunks. The second text modifies the first and the
+ * last chunk. This should be reported as one conflicting region.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testOneAgainstTwoConflictingModifications() throws IOException {
+ assertEquals(t("aZZcZefghij"),
+ merge("abcdefghij", "aZZZefghij", "aZcZefghij"));
+ }
+
+ /**
+ * Test a merge where only the second text contains modifications. Expect as
+ * merge result the second text.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testNoAgainstOneModification() throws IOException {
+ assertEquals(t("aZcZefghij"),
+ merge("abcdefghij", "abcdefghij", "aZcZefghij"));
+ }
+
+ /**
+ * Both texts contain modifications but not on the same chunks. Expect a
+ * non-conflict merge result.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testTwoNonConflictingModifications() throws IOException {
+ assertEquals(t("YbZdefghij"),
+ merge("abcdefghij", "abZdefghij", "Ybcdefghij"));
+ }
+
+ /**
+ * Merge two complicated modifications. The merge algorithm has to extend
+ * and combine conflicting regions to get to the expected merge result.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testTwoComplicatedModifications() throws IOException {
+ assertEquals(t("aZZZZfZhZjbYdYYYYiY"),
+ merge("abcdefghij", "aZZZZfZhZj", "abYdYYYYiY"));
+ }
+
+ /**
+ * Merge two modifications with a shared delete at the end. The underlying
+ * diff algorithm has to provide consistent edit results to get the expected
+ * merge result.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testTwoModificationsWithSharedDelete() throws IOException {
+ assertEquals(t("Cb}n}"), merge("ab}n}n}", "ab}n}", "Cb}n}"));
+ }
+
+ /**
+ * Merge modifications with a shared insert in the middle. The underlying
+ * diff algorithm has to provide consistent edit results to get the expected
+ * merge result.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testModificationsWithMiddleInsert() throws IOException {
+ assertEquals(t("aBcd123123uvwxPq"),
+ merge("abcd123uvwxpq", "aBcd123123uvwxPq", "abcd123123uvwxpq"));
+ }
+
+ /**
+ * Merge modifications with a shared delete in the middle. The underlying
+ * diff algorithm has to provide consistent edit results to get the expected
+ * merge result.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testModificationsWithMiddleDelete() throws IOException {
+ assertEquals(t("Abz}z123Q"),
+ merge("abz}z}z123q", "Abz}z123Q", "abz}z123q"));
+ }
+
+ @Test
+ public void testInsertionAfterDeletion() throws IOException {
+ assertEquals(t("abcd"), merge("abd", "ad", "abcd"));
+ }
+
+ @Test
+ public void testInsertionBeforeDeletion() throws IOException {
+ assertEquals(t("acbd"), merge("abd", "ad", "acbd"));
+ }
+
+ /**
+ * Test a conflicting region at the very start of the text.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testConflictAtStart() throws IOException {
+ assertEquals(t("ZYbcdefghij"),
+ merge("abcdefghij", "Zbcdefghij", "Ybcdefghij"));
+ }
+
+ /**
+ * Test a conflicting region at the very end of the text.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testConflictAtEnd() throws IOException {
+ assertEquals(t("abcdefghiZY"),
+ merge("abcdefghij", "abcdefghiZ", "abcdefghiY"));
+ }
+
+ /**
+ * Check for a conflict where the second text was changed similar to the
+ * first one, but the second texts modification covers one more line.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testSameModification() throws IOException {
+ assertEquals(t("abZdefghij"),
+ merge("abcdefghij", "abZdefghij", "abZdefghij"));
+ }
+
+ /**
+ * Check that a deleted vs. a modified line shows up as conflict (see Bug
+ * 328551)
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testDeleteVsModify() throws IOException {
+ assertEquals(t("abZdefghij"),
+ merge("abcdefghij", "abdefghij", "abZdefghij"));
+ }
+
+ @Test
+ public void testInsertVsModify() throws IOException {
+ assertEquals(t("abZXY"), merge("ab", "abZ", "aXY"));
+ }
+
+ @Test
+ public void testAdjacentModifications() throws IOException {
+ assertEquals(t("aZcbYd"), merge("abcd", "aZcd", "abYd"));
+ }
+
+ @Test
+ public void testSeparateModifications() throws IOException {
+ assertEquals(t("aZcYe"), merge("abcde", "aZcde", "abcYe"));
+ }
+
+ @Test
+ public void testBlankLines() throws IOException {
+ assertEquals(t("aZc\nYe"), merge("abc\nde", "aZc\nde", "abc\nYe"));
+ }
+
+ /**
+ * Test merging two contents which do one similar modification and one
+ * insertion is only done by one side, in the middle. Between modification
+ * and insertion is a block which is common between the two contents and the
+ * common base
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testTwoSimilarModsAndOneInsert() throws IOException {
+ assertEquals(t("aBcDde"), merge("abcde", "aBcde", "aBcDde"));
+
+ assertEquals(t("IAAAJCAB"), merge("iACAB", "IACAB", "IAAAJCAB"));
+
+ assertEquals(t("HIAAAJCAB"), merge("HiACAB", "HIACAB", "HIAAAJCAB"));
+
+ assertEquals(t("AGADEFHIAAAJCAB"),
+ merge("AGADEFHiACAB", "AGADEFHIACAB", "AGADEFHIAAAJCAB"));
+ }
+
+ /**
+ * Test merging two contents which do one similar modification and one
+ * insertion is only done by one side, at the end. Between modification and
+ * insertion is a block which is common between the two contents and the
+ * common base
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testTwoSimilarModsAndOneInsertAtEnd() throws IOException {
+ Assume.assumeTrue(newlineAtEnd);
+ assertEquals(t("IAAJ"), merge("iA", "IA", "IAAJ"));
+
+ assertEquals(t("IAJ"), merge("iA", "IA", "IAJ"));
+
+ assertEquals(t("IAAAJ"), merge("iA", "IA", "IAAAJ"));
+ }
+
+ @Test
+ public void testTwoSimilarModsAndOneInsertAtEndNoNewlineAtEnd()
+ throws IOException {
+ Assume.assumeFalse(newlineAtEnd);
+ assertEquals(t("IAAAJ"), merge("iA", "IA", "IAAJ"));
+
+ assertEquals(t("IAAJ"), merge("iA", "IA", "IAJ"));
+
+ assertEquals(t("IAAAAJ"), merge("iA", "IA", "IAAAJ"));
+ }
+
+ // Test situations where (at least) one input value is the empty text
+
+ @Test
+ public void testEmptyTextModifiedAgainstDeletion() throws IOException {
+ // NOTE: git.git merge-file appends a '\n' to the end of the file even
+ // when the input files do not have a newline at the end. That appears
+ // to be a bug in git.git.
+ assertEquals(t("AB"), merge("A", "AB", ""));
+ assertEquals(t("AB"), merge("A", "", "AB"));
+ }
+
+ @Test
+ public void testEmptyTextUnmodifiedAgainstDeletion() throws IOException {
+ assertEquals(t(""), merge("AB", "AB", ""));
+
+ assertEquals(t(""), merge("AB", "", "AB"));
+ }
+
+ @Test
+ public void testEmptyTextDeletionAgainstDeletion() throws IOException {
+ assertEquals(t(""), merge("AB", "", ""));
+ }
+
+ private String merge(String commonBase, String ours, String theirs)
+ throws IOException {
+ MergeAlgorithm ma = new MergeAlgorithm();
+ ma.setContentMergeStrategy(ContentMergeStrategy.UNION);
+ MergeResult<RawText> r = ma.merge(RawTextComparator.DEFAULT,
+ T(commonBase), T(ours), T(theirs));
+ ByteArrayOutputStream bo = new ByteArrayOutputStream(50);
+ fmt.formatMerge(bo, r, "B", "O", "T", UTF_8);
+ return bo.toString(UTF_8);
+ }
+
+ public String t(String text) {
+ StringBuilder r = new StringBuilder();
+ for (int i = 0; i < text.length(); i++) {
+ char c = text.charAt(i);
+ switch (c) {
+ case '<':
+ r.append("<<<<<<< O\n");
+ break;
+ case '=':
+ r.append("=======\n");
+ break;
+ case '|':
+ r.append("||||||| B\n");
+ break;
+ case '>':
+ r.append(">>>>>>> T\n");
+ break;
+ default:
+ r.append(c);
+ if (newlineAtEnd || i < text.length() - 1)
+ r.append('\n');
+ }
+ }
+ return r.toString();
+ }
+
+ public RawText T(String text) {
+ return new RawText(Constants.encode(t(text)));
+ }
+}
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 3a036acaca..c6a6321cf8 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
@@ -1792,7 +1792,77 @@ public class MergerTest extends RepositoryTestCase {
// children
mergeResult = git.merge().include(commitC3S).call();
assertEquals(mergeResult.getMergeStatus(), MergeStatus.MERGED);
+ }
+
+ /**
+ * Merging two commits when binary files have equal content, but conflicting content in the
+ * virtual ancestor.
+ *
+ * <p>
+ * This test has the same set up as
+ * {@code checkFileDirMergeConflictInVirtualAncestor_NoConflictInChildren}, only
+ * with the content conflict in A1 and A2.
+ */
+ @Theory
+ public void checkBinaryMergeConflictInVirtualAncestor(MergeStrategy strategy) throws Exception {
+ if (!strategy.equals(MergeStrategy.RECURSIVE)) {
+ return;
+ }
+
+ Git git = Git.wrap(db);
+
+ // master
+ writeTrashFile("c", "initial file");
+ git.add().addFilepattern("c").call();
+ RevCommit commitI = git.commit().setMessage("Initial commit").call();
+
+ writeTrashFile("a", "\0\1\1\1\1\0"); // content in Ancestor 1
+ git.add().addFilepattern("a").call();
+ RevCommit commitA1 = git.commit().setMessage("Ancestor 1").call();
+
+ writeTrashFile("a", "\0\1\2\3\4\5\0"); // 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();
+ writeTrashFile("a", "\0\2\2\2\2\0"); // content in Ancestor 1
+ 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", "\0\5\4\3\2\1\0"); // 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
+ writeTrashFile("a", "\0\3\3\3\3\0"); // 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 - set the same value as in resolution above
+ writeTrashFile("a", "\0\3\3\3\3\0"); // 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);
}
/**
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
index 798aebe3b0..0016adfb66 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
@@ -16,6 +16,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
@@ -375,7 +377,8 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
ObjectId[] parentIds) throws Exception {
CommitBuilder c = new CommitBuilder();
c.setTreeId(treeB.writeTree(odi));
- c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
+ c.setAuthor(new PersonIdent("A U Thor", "a.u.thor",
+ Instant.ofEpochMilli(1L), ZoneOffset.UTC));
c.setCommitter(c.getAuthor());
c.setParentIds(parentIds);
c.setMessage("Tree " + c.getTreeId().name());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java
index 2aac15bbb6..5507f8572d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java
@@ -48,8 +48,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
-@Suite.SuiteClasses({
- PatchApplierTest.WithWorktree. class, //
+@Suite.SuiteClasses({ PatchApplierTest.WithWorktree.class, //
PatchApplierTest.InCore.class, //
})
public class PatchApplierTest {
@@ -128,6 +127,20 @@ public class PatchApplierTest {
}
}
+ protected Result applyPatchAllowConflicts() throws IOException {
+ InputStream patchStream = getTestResource(name + ".patch");
+ Patch patch = new Patch();
+ patch.parse(patchStream);
+ if (inCore) {
+ try (ObjectInserter oi = db.newObjectInserter()) {
+ return new PatchApplier(db, baseTip, oi).allowConflicts()
+ .applyPatch(patch);
+ }
+ }
+ return new PatchApplier(db).allowConflicts()
+ .applyPatch(patch);
+ }
+
protected static InputStream getTestResource(String patchFile) {
return PatchApplierTest.class.getClassLoader()
.getResourceAsStream("org/eclipse/jgit/diff/" + patchFile);
@@ -169,6 +182,13 @@ public class PatchApplierTest {
verifyContent(result, aName, exists);
}
+ void verifyChange(Result result, String aName, boolean exists,
+ int numConflicts) throws Exception {
+ assertEquals(numConflicts, result.getErrors().size());
+ assertEquals(1, result.getPaths().size());
+ verifyContent(result, aName, exists);
+ }
+
protected byte[] readBlob(ObjectId treeish, String path)
throws Exception {
try (TestRepository<?> tr = new TestRepository<>(db);
@@ -346,6 +366,44 @@ public class PatchApplierTest {
}
@Test
+ public void testConflictMarkers() throws Exception {
+ init("allowconflict", true, true);
+
+ Result result = applyPatchAllowConflicts();
+
+ assertEquals(result.getErrors().size(), 1);
+ PatchApplier.Result.Error error = result.getErrors().get(0);
+ assertEquals("cannot apply hunk", error.msg);
+ assertEquals("allowconflict", error.oldFileName);
+ assertTrue(error.isGitConflict());
+ verifyChange(result, "allowconflict", true, 1);
+ }
+
+ @Test
+ public void testConflictMarkersOutOfBounds() throws Exception {
+ init("ConflictOutOfBounds", true, true);
+
+ Result result = applyPatchAllowConflicts();
+
+ assertEquals(result.getErrors().size(), 1);
+ PatchApplier.Result.Error error = result.getErrors().get(0);
+ assertEquals("cannot apply hunk", error.msg);
+ assertEquals("ConflictOutOfBounds", error.oldFileName);
+ assertTrue(error.isGitConflict());
+ verifyChange(result, "ConflictOutOfBounds", true, 1);
+ }
+
+ @Test
+ public void testConflictMarkersFileDeleted() throws Exception {
+ init("allowconflict_file_deleted", false, false);
+
+ Result result = applyPatchAllowConflicts();
+
+ assertEquals(1, result.getErrors().size());
+ assertEquals(0, result.getPaths().size());
+ }
+
+ @Test
public void testShiftUp() throws Exception {
init("ShiftUp");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
index 6872289a8b..014ff928a8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009, Google Inc. and others
+ * Copyright (C) 2008, 2024 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
@@ -23,7 +23,9 @@ import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
-import java.util.TimeZone;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.CommitBuilder;
@@ -94,18 +96,17 @@ public class RevCommitParseTest extends RepositoryTestCase {
assertNotNull(cAuthor);
assertEquals(authorName, cAuthor.getName());
assertEquals(authorEmail, cAuthor.getEmailAddress());
- assertEquals((long) authorTime * 1000, cAuthor.getWhen().getTime());
- assertEquals(TimeZone.getTimeZone("GMT" + authorTimeZone),
- cAuthor.getTimeZone());
+ assertEquals(Instant.ofEpochSecond(authorTime),
+ cAuthor.getWhenAsInstant());
+ assertEquals(ZoneId.of(authorTimeZone), cAuthor.getZoneId());
final PersonIdent cCommitter = c.getCommitterIdent();
assertNotNull(cCommitter);
assertEquals(committerName, cCommitter.getName());
assertEquals(committerEmail, cCommitter.getEmailAddress());
- assertEquals((long) committerTime * 1000,
- cCommitter.getWhen().getTime());
- assertEquals(TimeZone.getTimeZone("GMT" + committerTimeZone),
- cCommitter.getTimeZone());
+ assertEquals(Instant.ofEpochSecond(committerTime),
+ cCommitter.getWhenAsInstant());
+ assertEquals(ZoneId.of(committerTimeZone), cCommitter.getZoneId());
}
private RevCommit create(String msg) throws Exception {
@@ -153,9 +154,13 @@ public class RevCommitParseTest extends RepositoryTestCase {
c.parseCanonical(rw, b.toString().getBytes(UTF_8));
}
assertEquals(
- new PersonIdent("", "a_u_thor@example.com", 1218123387000L, 7),
+ new PersonIdent("", "a_u_thor@example.com",
+ Instant.ofEpochMilli(1218123387000L),
+ ZoneOffset.ofHoursMinutes(0, 7)),
c.getAuthorIdent());
- assertEquals(new PersonIdent("", "", 1218123390000L, -5),
+ assertEquals(
+ new PersonIdent("", "", Instant.ofEpochMilli(1218123390000L),
+ ZoneOffset.ofHoursMinutes(0, -5)),
c.getCommitterIdent());
}
@@ -408,6 +413,7 @@ public class RevCommitParseTest extends RepositoryTestCase {
final RevCommit c = create(msg);
assertEquals(msg, c.getFullMessage());
assertEquals(msg, c.getShortMessage());
+ assertEquals(msg, c.getFirstMessageLine());
}
@Test
@@ -415,6 +421,7 @@ public class RevCommitParseTest extends RepositoryTestCase {
final RevCommit c = create("\n");
assertEquals("\n", c.getFullMessage());
assertEquals("", c.getShortMessage());
+ assertEquals("", c.getFirstMessageLine());
}
@Test
@@ -423,6 +430,7 @@ public class RevCommitParseTest extends RepositoryTestCase {
final RevCommit c = create(shortMsg);
assertEquals(shortMsg, c.getFullMessage());
assertEquals(shortMsg, c.getShortMessage());
+ assertEquals(shortMsg, c.getFirstMessageLine());
}
@Test
@@ -432,6 +440,7 @@ public class RevCommitParseTest extends RepositoryTestCase {
final RevCommit c = create(fullMsg);
assertEquals(fullMsg, c.getFullMessage());
assertEquals(shortMsg, c.getShortMessage());
+ assertEquals(shortMsg, c.getFirstMessageLine());
}
@Test
@@ -441,6 +450,7 @@ public class RevCommitParseTest extends RepositoryTestCase {
final RevCommit c = create(fullMsg);
assertEquals(fullMsg, c.getFullMessage());
assertEquals(shortMsg, c.getShortMessage());
+ assertEquals("This is a", c.getFirstMessageLine());
}
@Test
@@ -450,6 +460,7 @@ public class RevCommitParseTest extends RepositoryTestCase {
final RevCommit c = create(fullMsg);
assertEquals(fullMsg, c.getFullMessage());
assertEquals(shortMsg, c.getShortMessage());
+ assertEquals("This is a", c.getFirstMessageLine());
}
@Test
@@ -461,6 +472,7 @@ public class RevCommitParseTest extends RepositoryTestCase {
final RevCommit c = create(fullMsg);
assertEquals(fullMsg, c.getFullMessage());
assertEquals(shortMsg, c.getShortMessage());
+ assertEquals(shortMsg, c.getFirstMessageLine());
}
@Test
@@ -480,6 +492,7 @@ public class RevCommitParseTest extends RepositoryTestCase {
assertEquals(author, p.getAuthorIdent());
assertEquals(committer, p.getCommitterIdent());
assertEquals("Test commit", p.getShortMessage());
+ assertEquals("Test commit", p.getFirstMessageLine());
assertEquals(src.getMessage(), p.getFullMessage());
}
@@ -494,6 +507,7 @@ public class RevCommitParseTest extends RepositoryTestCase {
final RevCommit c = create(fullMsg);
assertEquals(fullMsg, c.getFullMessage());
assertEquals(shortMsg, c.getShortMessage());
+ assertEquals("This fixes a", c.getFirstMessageLine());
}
private static ObjectId id(String str) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java
index 81ff4a2f92..7fece66bf0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java
@@ -14,6 +14,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.io.IOException;
+import java.time.Instant;
import java.util.Date;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -217,14 +218,132 @@ public class RevWalkFilterTest extends RevWalkTestCase {
final RevCommit b = commit(a);
tick(100);
- Date since = getDate();
+ Instant since = getInstant();
final RevCommit c1 = commit(b);
tick(100);
final RevCommit c2 = commit(b);
tick(100);
- Date until = getDate();
+ Instant until = getInstant();
+ final RevCommit d = commit(c1, c2);
+ tick(100);
+
+ final RevCommit e = commit(d);
+
+ {
+ RevFilter after = CommitTimeRevFilter.after(since);
+ assertNotNull(after);
+ rw.setRevFilter(after);
+ markStart(e);
+ assertCommit(e, rw.next());
+ assertCommit(d, rw.next());
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertNull(rw.next());
+ }
+
+ {
+ RevFilter before = CommitTimeRevFilter.before(until);
+ assertNotNull(before);
+ rw.reset();
+ rw.setRevFilter(before);
+ markStart(e);
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertCommit(b, rw.next());
+ assertCommit(a, rw.next());
+ assertNull(rw.next());
+ }
+
+ {
+ RevFilter between = CommitTimeRevFilter.between(since, until);
+ assertNotNull(between);
+ rw.reset();
+ rw.setRevFilter(between);
+ markStart(e);
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertNull(rw.next());
+ }
+ }
+
+ @Test
+ public void testCommitTimeRevFilter_date() throws Exception {
+ // Using deprecated Date api for the commit time rev filter.
+ // Delete this tests when method is removed.
+ final RevCommit a = commit();
+ tick(100);
+
+ final RevCommit b = commit(a);
+ tick(100);
+
+ Date since = Date.from(getInstant());
+ final RevCommit c1 = commit(b);
+ tick(100);
+
+ final RevCommit c2 = commit(b);
+ tick(100);
+
+ Date until = Date.from(getInstant());
+ final RevCommit d = commit(c1, c2);
+ tick(100);
+
+ final RevCommit e = commit(d);
+
+ {
+ RevFilter after = CommitTimeRevFilter.after(since);
+ assertNotNull(after);
+ rw.setRevFilter(after);
+ markStart(e);
+ assertCommit(e, rw.next());
+ assertCommit(d, rw.next());
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertNull(rw.next());
+ }
+
+ {
+ RevFilter before = CommitTimeRevFilter.before(until);
+ assertNotNull(before);
+ rw.reset();
+ rw.setRevFilter(before);
+ markStart(e);
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertCommit(b, rw.next());
+ assertCommit(a, rw.next());
+ assertNull(rw.next());
+ }
+
+ {
+ RevFilter between = CommitTimeRevFilter.between(since, until);
+ assertNotNull(between);
+ rw.reset();
+ rw.setRevFilter(between);
+ markStart(e);
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertNull(rw.next());
+ }
+ }
+
+ @Test
+ public void testCommitTimeRevFilter_long() throws Exception {
+ final RevCommit a = commit();
+ tick(100);
+
+ final RevCommit b = commit(a);
+ tick(100);
+
+ long since = getInstant().toEpochMilli();
+ final RevCommit c1 = commit(b);
+ tick(100);
+
+ final RevCommit c2 = commit(b);
+ tick(100);
+
+ long until = getInstant().toEpochMilli();
final RevCommit d = commit(c1, c2);
tick(100);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
index ec0c0e7e84..8fa6a83670 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
@@ -12,6 +12,7 @@ package org.eclipse.jgit.revwalk;
import static org.junit.Assert.assertSame;
+import java.time.Instant;
import java.util.Date;
import org.eclipse.jgit.dircache.DirCacheEntry;
@@ -38,8 +39,14 @@ public abstract class RevWalkTestCase extends RepositoryTestCase {
return new RevWalk(db);
}
+ // Use getInstant() instead
+ @Deprecated
protected Date getDate() {
- return util.getDate();
+ return Date.from(util.getInstant());
+ }
+
+ protected Instant getInstant() {
+ return util.getInstant();
}
protected void tick(int secDelta) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java
index 0a045c917b..ffc7c96f69 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java
@@ -14,6 +14,7 @@ import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import org.eclipse.jgit.api.Git;
@@ -121,7 +122,7 @@ public class RevWalkUtilsReachableTest extends RevWalkTestCase {
Collection<Ref> sortedRefs = RefComparator.sort(allRefs);
List<Ref> actual = RevWalkUtils.findBranchesReachableFrom(commit,
rw, sortedRefs);
- assertEquals(refsThatShouldContainCommit, actual);
+ assertEquals(new HashSet<>(refsThatShouldContainCommit), new HashSet<>(actual));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java
index 300c869b78..4306975f7b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java
@@ -114,6 +114,13 @@ public class SubmoduleAddTest extends RepositoryTestCase {
try (Repository subModRepo = generator.getRepository()) {
assertNotNull(subModRepo);
assertEquals(subCommit, commit);
+ String worktreeDir = subModRepo.getConfig().getString(
+ ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_WORKTREE);
+ assertEquals("../../../sub", worktreeDir);
+ String gitdir = read(new File(subModRepo.getWorkTree(),
+ Constants.DOT_GIT));
+ assertEquals("gitdir: ../.git/modules/sub", gitdir);
}
}
Status status = Git.wrap(db).status().call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java
index b10bd73208..d54117005d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java
@@ -17,21 +17,25 @@ import java.io.File;
import java.io.IOException;
import java.util.Collection;
+import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.InitCommand;
+import org.eclipse.jgit.api.SubmoduleAddCommand;
import org.eclipse.jgit.api.SubmoduleUpdateCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
-import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.junit.Test;
@@ -40,6 +44,91 @@ import org.junit.Test;
*/
public class SubmoduleUpdateTest extends RepositoryTestCase {
+ private Repository submoduleRepo;
+
+ private Git git;
+
+ private AnyObjectId subRepoCommit2;
+
+ private void createSubmoduleRepo() throws IOException, GitAPIException {
+ File directory = createTempDirectory("submodule_repo");
+ InitCommand init = Git.init();
+ init.setDirectory(directory);
+ init.call();
+ submoduleRepo = Git.open(directory).getRepository();
+ try (Git sub = Git.wrap(submoduleRepo)) {
+ // commit something
+ JGitTestUtil.writeTrashFile(submoduleRepo, "commit1.txt",
+ "commit 1");
+ sub.add().addFilepattern("commit1.txt").call();
+ sub.commit().setMessage("commit 1").call().getId();
+
+ JGitTestUtil.writeTrashFile(submoduleRepo, "commit2.txt",
+ "commit 2");
+ sub.add().addFilepattern("commit2.txt").call();
+ subRepoCommit2 = sub.commit().setMessage("commit 2").call().getId();
+ }
+ }
+
+ private void addSubmodule(String path) throws GitAPIException {
+ SubmoduleAddCommand command = new SubmoduleAddCommand(db);
+ command.setPath(path);
+ String uri = submoduleRepo.getDirectory().toURI().toString();
+ command.setURI(uri);
+ try (Repository repo = command.call()) {
+ assertNotNull(repo);
+ }
+ git.add().addFilepattern(path).addFilepattern(Constants.DOT_GIT_MODULES)
+ .call();
+ git.commit().setMessage("adding submodule").call();
+ recursiveDelete(new File(git.getRepository().getWorkTree(), path));
+ recursiveDelete(
+ new File(new File(git.getRepository().getCommonDirectory(),
+ Constants.MODULES), path));
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ createSubmoduleRepo();
+
+ git = Git.wrap(db);
+ // commit something
+ writeTrashFile("initial.txt", "initial");
+ git.add().addFilepattern("initial.txt").call();
+ git.commit().setMessage("initial commit").call();
+ }
+
+ public void updateModeClonedRestoredSubmoduleTemplate(String mode)
+ throws Exception {
+ String path = "sub";
+ addSubmodule(path);
+
+ StoredConfig cfg = git.getRepository().getConfig();
+ if (mode != null) {
+ cfg.load();
+ cfg.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_UPDATE, mode);
+ cfg.save();
+ }
+ SubmoduleUpdateCommand update = new SubmoduleUpdateCommand(db);
+ update.call();
+ try (Git subGit = Git.open(new File(db.getWorkTree(), path))) {
+ update.call();
+ assertEquals(subRepoCommit2.getName(),
+ subGit.getRepository().getBranch());
+ }
+
+ recursiveDelete(new File(db.getWorkTree(), path));
+
+ update.call();
+ try (Git subGit = Git.open(new File(db.getWorkTree(), path))) {
+ update.call();
+ assertEquals(subRepoCommit2.getName(),
+ subGit.getRepository().getBranch());
+ }
+ }
+
@Test
public void repositoryWithNoSubmodules() throws GitAPIException {
SubmoduleUpdateCommand command = new SubmoduleUpdateCommand(db);
@@ -50,35 +139,9 @@ public class SubmoduleUpdateTest extends RepositoryTestCase {
@Test
public void repositoryWithSubmodule() throws Exception {
- writeTrashFile("file.txt", "content");
- Git git = Git.wrap(db);
- git.add().addFilepattern("file.txt").call();
- final RevCommit commit = git.commit().setMessage("create file").call();
final String path = "sub";
- DirCache cache = db.lockDirCache();
- DirCacheEditor editor = cache.editor();
- editor.add(new PathEdit(path) {
-
- @Override
- public void apply(DirCacheEntry ent) {
- ent.setFileMode(FileMode.GITLINK);
- ent.setObjectId(commit);
- }
- });
- editor.commit();
-
- StoredConfig config = db.getConfig();
- config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
- ConfigConstants.CONFIG_KEY_URL, db.getDirectory().toURI()
- .toString());
- config.save();
-
- FileBasedConfig modulesConfig = new FileBasedConfig(new File(
- db.getWorkTree(), Constants.DOT_GIT_MODULES), db.getFS());
- modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
- ConfigConstants.CONFIG_KEY_PATH, path);
- modulesConfig.save();
+ addSubmodule(path);
SubmoduleUpdateCommand command = new SubmoduleUpdateCommand(db);
Collection<String> updated = command.call();
@@ -90,14 +153,22 @@ public class SubmoduleUpdateTest extends RepositoryTestCase {
assertTrue(generator.next());
try (Repository subRepo = generator.getRepository()) {
assertNotNull(subRepo);
- assertEquals(commit, subRepo.resolve(Constants.HEAD));
+ assertEquals(subRepoCommit2, subRepo.resolve(Constants.HEAD));
+ String worktreeDir = subRepo.getConfig().getString(
+ ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_WORKTREE);
+ assertEquals("../../../sub", worktreeDir);
+ String gitdir = read(
+ new File(subRepo.getWorkTree(), Constants.DOT_GIT));
+ assertEquals("gitdir: ../.git/modules/sub", gitdir);
+
}
}
}
@Test
- public void repositoryWithUnconfiguredSubmodule() throws IOException,
- GitAPIException {
+ public void repositoryWithUnconfiguredSubmodule()
+ throws IOException, GitAPIException {
final ObjectId id = ObjectId
.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
final String path = "sub";
@@ -113,16 +184,14 @@ public class SubmoduleUpdateTest extends RepositoryTestCase {
});
editor.commit();
- FileBasedConfig modulesConfig = new FileBasedConfig(new File(
- db.getWorkTree(), Constants.DOT_GIT_MODULES), db.getFS());
+ FileBasedConfig modulesConfig = new FileBasedConfig(
+ new File(db.getWorkTree(), Constants.DOT_GIT_MODULES),
+ db.getFS());
modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
ConfigConstants.CONFIG_KEY_PATH, path);
String url = "git://server/repo.git";
modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
ConfigConstants.CONFIG_KEY_URL, url);
- String update = "rebase";
- modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
- ConfigConstants.CONFIG_KEY_UPDATE, update);
modulesConfig.save();
SubmoduleUpdateCommand command = new SubmoduleUpdateCommand(db);
@@ -132,8 +201,8 @@ public class SubmoduleUpdateTest extends RepositoryTestCase {
}
@Test
- public void repositoryWithInitializedSubmodule() throws IOException,
- GitAPIException {
+ public void repositoryWithInitializedSubmodule()
+ throws IOException, GitAPIException {
final ObjectId id = ObjectId
.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
final String path = "sub";
@@ -160,4 +229,77 @@ public class SubmoduleUpdateTest extends RepositoryTestCase {
assertNotNull(updated);
assertTrue(updated.isEmpty());
}
+
+ @Test
+ public void updateModeMergeClonedRestoredSubmodule() throws Exception {
+ updateModeClonedRestoredSubmoduleTemplate(
+ ConfigConstants.CONFIG_KEY_MERGE);
+ }
+
+ @Test
+ public void updateModeRebaseClonedRestoredSubmodule() throws Exception {
+ updateModeClonedRestoredSubmoduleTemplate(
+ ConfigConstants.CONFIG_KEY_REBASE);
+ }
+
+ @Test
+ public void updateModeCheckoutClonedRestoredSubmodule() throws Exception {
+ updateModeClonedRestoredSubmoduleTemplate(
+ ConfigConstants.CONFIG_KEY_CHECKOUT);
+ }
+
+ @Test
+ public void updateModeMissingClonedRestoredSubmodule() throws Exception {
+ updateModeClonedRestoredSubmoduleTemplate(null);
+ }
+
+ @Test
+ public void updateMode() throws Exception {
+ String path = "sub";
+ addSubmodule(path);
+
+ StoredConfig cfg = git.getRepository().getConfig();
+ cfg.load();
+ cfg.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_UPDATE,
+ ConfigConstants.CONFIG_KEY_REBASE);
+ cfg.save();
+
+ SubmoduleUpdateCommand update = new SubmoduleUpdateCommand(db);
+ update.call();
+ try (Git subGit = Git.open(new File(db.getWorkTree(), path))) {
+ CheckoutCommand checkout = subGit.checkout();
+ checkout.setName("master");
+ checkout.call();
+ update.call();
+ assertEquals("master", subGit.getRepository().getBranch());
+ }
+
+ cfg.load();
+ cfg.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_UPDATE,
+ ConfigConstants.CONFIG_KEY_CHECKOUT);
+ cfg.save();
+
+ update.call();
+ try (Git subGit = Git.open(new File(db.getWorkTree(), path))) {
+ assertEquals(subRepoCommit2.getName(),
+ subGit.getRepository().getBranch());
+ }
+
+ cfg.load();
+ cfg.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_UPDATE,
+ ConfigConstants.CONFIG_KEY_MERGE);
+ cfg.save();
+
+ update.call();
+ try (Git subGit = Git.open(new File(db.getWorkTree(), path))) {
+ CheckoutCommand checkout = subGit.checkout();
+ checkout.setName("master");
+ checkout.call();
+ update.call();
+ assertEquals("master", subGit.getRepository().getBranch());
+ }
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
index c47e591445..0ba8926a7f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
@@ -25,10 +25,6 @@ import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
-import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -46,16 +42,8 @@ public class AtomicPushTest {
public void setUp() throws Exception {
server = newRepo("server");
client = newRepo("client");
- testProtocol = new TestProtocol<>(
- null,
- new ReceivePackFactory<Object>() {
- @Override
- public ReceivePack create(Object req, Repository db)
- throws ServiceNotEnabledException,
- ServiceNotAuthorizedException {
- return new ReceivePack(db);
- }
- });
+ testProtocol = new TestProtocol<>(null,
+ (req, db) -> new ReceivePack(db));
uri = testProtocol.register(ctx, server);
try (TestRepository<?> clientRepo = new TestRepository<>(client)) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateIdentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateIdentTest.java
index cee023d5a9..6290b7978e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateIdentTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateIdentTest.java
@@ -138,6 +138,51 @@ public class PushCertificateIdentTest {
"Me <me@example.com>");
}
+ @Test
+ public void timezoneRange_hours() {
+ int HOUR_TO_MS = 60 * 60 * 1000;
+
+ // java.util.TimeZone: Hours must be between 0 to 23
+ PushCertificateIdent hourLimit = PushCertificateIdent
+ .parse("A U. Thor <a_u_thor@example.com> 1218123387 +2300");
+ assertEquals(1380, hourLimit.getTimeZoneOffset());
+ assertEquals(23 * HOUR_TO_MS,
+ hourLimit.getTimeZone().getOffset(1218123387));
+
+ PushCertificateIdent hourDubious = PushCertificateIdent
+ .parse("A U. Thor <a_u_thor@example.com> 1218123387 +2400");
+ assertEquals(1440, hourDubious.getTimeZoneOffset());
+ assertEquals(0, hourDubious.getTimeZone().getOffset(1218123387));
+ }
+
+ @Test
+ public void timezoneRange_minutes() {
+ PushCertificateIdent hourLimit = PushCertificateIdent
+ .parse("A U. Thor <a_u_thor@example.com> 1218123387 +0059");
+ assertEquals(59, hourLimit.getTimeZoneOffset());
+ assertEquals(59 * 60 * 1000,
+ hourLimit.getTimeZone().getOffset(1218123387));
+
+ // This becomes one hour and one minute (!)
+ PushCertificateIdent hourDubious = PushCertificateIdent
+ .parse("A U. Thor <a_u_thor@example.com> 1218123387 +0061");
+ assertEquals(61, hourDubious.getTimeZoneOffset());
+ assertEquals(61 * 60 * 1000,
+ hourDubious.getTimeZone().getOffset(1218123387));
+
+ PushCertificateIdent weirdCase = PushCertificateIdent
+ .parse("A U. Thor <a_u_thor@example.com> 1218123387 +0099");
+ assertEquals(99, weirdCase.getTimeZoneOffset());
+ assertEquals(99 * 60 * 1000,
+ weirdCase.getTimeZone().getOffset(1218123387));
+
+ PushCertificateIdent weirdCase2 = PushCertificateIdent
+ .parse("A U. Thor <a_u_thor@example.com> 1218123387 +0199");
+ assertEquals(60 + 99, weirdCase2.getTimeZoneOffset());
+ assertEquals((60 + 99) * 60 * 1000,
+ weirdCase2.getTimeZone().getOffset(1218123387));
+ }
+
private static void assertMatchesPersonIdent(String raw,
PersonIdent expectedPersonIdent, String expectedUserId) {
PushCertificateIdent certIdent = PushCertificateIdent.parse(raw);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java
index 4f01e4d445..a03222be0c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java
@@ -23,6 +23,8 @@ import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -318,8 +320,8 @@ public class PushCertificateStoreTest {
}
private PersonIdent newIdent() {
- return new PersonIdent(
- "A U. Thor", "author@example.com", ts.getAndIncrement(), 0);
+ return new PersonIdent("A U. Thor", "author@example.com",
+ Instant.ofEpochMilli(ts.getAndIncrement()), ZoneOffset.UTC);
}
private PushCertificateStore newStore() {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java
index 029b45e1e6..96d3a5835a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java
@@ -14,10 +14,10 @@ import static org.hamcrest.MatcherAssert.assertThat;
import java.io.File;
import java.io.IOException;
import java.net.HttpCookie;
+import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -101,7 +101,7 @@ public class TransportHttpTest extends SampleDataRepositoryTestCase {
.singletonList("cookie2=some value; Max-Age=1234; Path=/"));
try (TransportHttp transportHttp = new TransportHttp(db, uri)) {
- Date creationDate = new Date();
+ Instant creationDate = Instant.now();
transportHttp.processResponseCookies(connection);
// evaluate written cookie file
@@ -112,8 +112,9 @@ public class TransportHttpTest extends SampleDataRepositoryTestCase {
cookie.setPath("/u/2/");
cookie.setMaxAge(
- (Instant.parse("2100-01-01T11:00:00.000Z").toEpochMilli()
- - creationDate.getTime()) / 1000);
+ Duration.between(creationDate,
+ Instant.parse("2100-01-01T11:00:00.000Z"))
+ .getSeconds());
cookie.setSecure(true);
cookie.setHttpOnly(true);
expectedCookies.add(cookie);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
index d403624b71..67920029d4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
@@ -82,6 +82,43 @@ public class URIishTest {
}
@Test
+ public void testBrokenFilePath() throws Exception {
+ String str = "D:\\\\my\\\\x";
+ URIish u = new URIish(str);
+ assertNull(u.getScheme());
+ assertFalse(u.isRemote());
+ assertEquals(str, u.getPath());
+ assertEquals(u, new URIish(str));
+ }
+
+ @Test
+ public void testStackOverflow() throws Exception {
+ StringBuilder b = new StringBuilder("D:\\");
+ for (int i = 0; i < 4000; i++) {
+ b.append("x\\");
+ }
+ String str = b.toString();
+ URIish u = new URIish(str);
+ assertNull(u.getScheme());
+ assertFalse(u.isRemote());
+ assertEquals(str, u.getPath());
+ }
+
+ @Test
+ public void testStackOverflow2() throws Exception {
+ StringBuilder b = new StringBuilder("D:\\");
+ for (int i = 0; i < 4000; i++) {
+ b.append("x\\");
+ }
+ b.append('y');
+ String str = b.toString();
+ URIish u = new URIish(str);
+ assertNull(u.getScheme());
+ assertFalse(u.isRemote());
+ assertEquals(str, u.getPath());
+ }
+
+ @Test
public void testRelativePath() throws Exception {
final String str = "../../foo/bar";
URIish u = new URIish(str);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackReachabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackReachabilityTest.java
index 2711762640..a5507c8f81 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackReachabilityTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackReachabilityTest.java
@@ -27,9 +27,6 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.UploadPack.RequestPolicy;
-import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
-import org.eclipse.jgit.transport.resolver.UploadPackFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -264,15 +261,10 @@ public class UploadPackReachabilityTest {
}
private static TestProtocol<Object> generateReachableCommitUploadPackProtocol() {
- return new TestProtocol<>(new UploadPackFactory<Object>() {
- @Override
- public UploadPack create(Object req, Repository db)
- throws ServiceNotEnabledException,
- ServiceNotAuthorizedException {
- UploadPack up = new UploadPack(db);
- up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
- return up;
- }
+ return new TestProtocol<>((req, db) -> {
+ UploadPack up = new UploadPack(db);
+ up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
+ return up;
}, null);
}
}
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 def73acadd..5c2f0e5c7d 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
@@ -1,5 +1,6 @@
package org.eclipse.jgit.transport;
+import static java.time.ZoneOffset.UTC;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
@@ -11,12 +12,14 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -501,15 +504,15 @@ public class UploadPackTest {
assertThat(hook.capabilitiesRequest, notNullValue());
assertThat(pckIn.readString(), is("version 2"));
assertThat(
- Arrays.asList(pckIn.readString(), pckIn.readString(),
- pckIn.readString()),
+ Arrays.asList(pckIn.readString(),pckIn.readString(),
+ pckIn.readString(), pckIn.readString()),
// TODO(jonathantanmy) This check is written this way
// to make it simple to see that we expect this list of
// capabilities, but probably should be loosened to
// allow additional commands to be added to the list,
// and additional capabilities to be added to existing
// commands without requiring test changes.
- hasItems("ls-refs", "fetch=shallow", "server-option"));
+ hasItems("agent=" + UserAgent.get() ,"ls-refs", "fetch=shallow", "server-option"));
assertTrue(PacketLineIn.isEnd(pckIn.readString()));
}
@@ -535,7 +538,7 @@ public class UploadPackTest {
lines.add(line);
}
}
- assertThat(lines, containsInAnyOrder("ls-refs", "fetch", "server-option"));
+ assertThat(lines, containsInAnyOrder("ls-refs", "fetch", "server-option", "agent=" + UserAgent.get()));
}
private void checkUnadvertisedIfUnallowed(String configSection,
@@ -564,6 +567,47 @@ public class UploadPackTest {
}
@Test
+ public void testV0CapabilitiesAllowAnySha1InWant() throws Exception {
+ checkAvertisedCapabilityProtocolV0IfAllowed("uploadpack",
+ "allowanysha1inwant", "allow-reachable-sha1-in-want",
+ "allow-tip-sha1-in-want");
+ }
+
+ @Test
+ public void testV0CapabilitiesAllowReachableSha1InWant() throws Exception {
+ checkAvertisedCapabilityProtocolV0IfAllowed("uploadpack",
+ "allowreachablesha1inwant", "allow-reachable-sha1-in-want");
+ }
+
+ @Test
+ public void testV0CapabilitiesAllowTipSha1InWant() throws Exception {
+ checkAvertisedCapabilityProtocolV0IfAllowed("uploadpack",
+ "allowtipsha1inwant", "allow-tip-sha1-in-want");
+ }
+
+ private void checkAvertisedCapabilityProtocolV0IfAllowed(
+ String configSection, String configName, String... capabilities)
+ throws Exception {
+ server.getConfig().setBoolean(configSection, null, configName, true);
+ ByteArrayInputStream recvStream = uploadPackSetup(
+ TransferConfig.ProtocolVersion.V0.version(), null,
+ PacketLineIn.end());
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+
+ String line;
+ while (!PacketLineIn.isEnd((line = pckIn.readString()))) {
+ if (line.contains("capabilities")) {
+ List<String> linesCapabilities = Arrays.asList(line.substring(
+ line.indexOf(" ", line.indexOf("capabilities")) + 1)
+ .split(" "));
+ assertThat(linesCapabilities, hasItems(capabilities));
+ return;
+ }
+ }
+ fail("Server side protocol did not contain any capabilities'");
+ }
+
+ @Test
public void testV2CapabilitiesAllowFilter() throws Exception {
checkAdvertisedIfAllowed("uploadpack", "allowfilter", "filter");
checkUnadvertisedIfUnallowed("uploadpack", "allowfilter", "filter");
@@ -601,9 +645,9 @@ public class UploadPackTest {
assertThat(pckIn.readString(), is("version 2"));
assertThat(
- Arrays.asList(pckIn.readString(), pckIn.readString(),
+ Arrays.asList(pckIn.readString(),pckIn.readString(), pckIn.readString(),
pckIn.readString()),
- hasItems("ls-refs", "fetch=shallow", "server-option"));
+ hasItems("agent="+ UserAgent.get(),"ls-refs", "fetch=shallow", "server-option"));
assertTrue(PacketLineIn.isEnd(pckIn.readString()));
}
@@ -1464,14 +1508,19 @@ public class UploadPackTest {
public void testV2FetchShallowSince() throws Exception {
PersonIdent person = new PersonIdent(remote.getRepository());
- RevCommit beyondBoundary = remote.commit()
- .committer(new PersonIdent(person, 1510000000, 0)).create();
- RevCommit boundary = remote.commit().parent(beyondBoundary)
- .committer(new PersonIdent(person, 1520000000, 0)).create();
- RevCommit tooOld = remote.commit()
- .committer(new PersonIdent(person, 1500000000, 0)).create();
+ RevCommit beyondBoundary = remote.commit().committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1510000), UTC))
+ .create();
+ RevCommit boundary = remote.commit().parent(beyondBoundary).committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1520000), UTC))
+ .create();
+ RevCommit tooOld = remote.commit().committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1500000), UTC))
+ .create();
RevCommit merge = remote.commit().parent(boundary).parent(tooOld)
- .committer(new PersonIdent(person, 1530000000, 0)).create();
+ .committer(new PersonIdent(person,
+ Instant.ofEpochSecond(1530000), UTC))
+ .create();
remote.update("branch1", merge);
@@ -1517,12 +1566,15 @@ public class UploadPackTest {
public void testV2FetchShallowSince_excludedParentWithMultipleChildren() throws Exception {
PersonIdent person = new PersonIdent(remote.getRepository());
- RevCommit base = remote.commit()
- .committer(new PersonIdent(person, 1500000000, 0)).create();
- RevCommit child1 = remote.commit().parent(base)
- .committer(new PersonIdent(person, 1510000000, 0)).create();
- RevCommit child2 = remote.commit().parent(base)
- .committer(new PersonIdent(person, 1520000000, 0)).create();
+ RevCommit base = remote.commit().committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1500000), UTC))
+ .create();
+ RevCommit child1 = remote.commit().parent(base).committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1510000), UTC))
+ .create();
+ RevCommit child2 = remote.commit().parent(base).committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1520000), UTC))
+ .create();
remote.update("branch1", child1);
remote.update("branch2", child2);
@@ -1559,8 +1611,9 @@ public class UploadPackTest {
public void testV2FetchShallowSince_noCommitsSelected() throws Exception {
PersonIdent person = new PersonIdent(remote.getRepository());
- RevCommit tooOld = remote.commit()
- .committer(new PersonIdent(person, 1500000000, 0)).create();
+ RevCommit tooOld = remote.commit().committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1500000), UTC))
+ .create();
remote.update("branch1", tooOld);
@@ -1684,12 +1737,15 @@ public class UploadPackTest {
public void testV2FetchDeepenNot_excludedParentWithMultipleChildren() throws Exception {
PersonIdent person = new PersonIdent(remote.getRepository());
- RevCommit base = remote.commit()
- .committer(new PersonIdent(person, 1500000000, 0)).create();
- RevCommit child1 = remote.commit().parent(base)
- .committer(new PersonIdent(person, 1510000000, 0)).create();
- RevCommit child2 = remote.commit().parent(base)
- .committer(new PersonIdent(person, 1520000000, 0)).create();
+ RevCommit base = remote.commit().committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1500000), UTC))
+ .create();
+ RevCommit child1 = remote.commit().parent(base).committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1510000), UTC))
+ .create();
+ RevCommit child2 = remote.commit().parent(base).committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1520000), UTC))
+ .create();
remote.update("base", base);
remote.update("branch1", child1);
@@ -2820,7 +2876,7 @@ public class UploadPackTest {
RevTag heavyTag2 = remote.tag("middleTagRing", heavyTag1);
remote.lightweightTag("refTagRing", heavyTag2);
- UploadPack uploadPack = new UploadPack(remote.getRepository());
+ try (UploadPack uploadPack = new UploadPack(remote.getRepository())) {
ByteArrayOutputStream cli = new ByteArrayOutputStream();
PacketLineOut clientWant = new PacketLineOut(cli);
@@ -2830,7 +2886,6 @@ public class UploadPackTest {
clientWant.writeString("done\n");
try (ByteArrayOutputStream serverResponse = new ByteArrayOutputStream()) {
-
uploadPack.setPreUploadHook(new PreUploadHook() {
@Override
public void onBeginNegotiateRound(UploadPack up,
@@ -2883,6 +2938,7 @@ public class UploadPackTest {
assertTrue(objDb.has(heavyTag2.toObjectId()));
}
}
+}
@Test
public void testSingleBranchShallowCloneTagChainWithReflessTag() throws Exception {
@@ -2894,7 +2950,7 @@ public class UploadPackTest {
RevTag tag3 = remote.tag("t3", tag2);
remote.lightweightTag("t3", tag3);
- UploadPack uploadPack = new UploadPack(remote.getRepository());
+ try (UploadPack uploadPack = new UploadPack(remote.getRepository())) {
ByteArrayOutputStream cli = new ByteArrayOutputStream();
PacketLineOut clientWant = new PacketLineOut(cli);
@@ -2904,7 +2960,6 @@ public class UploadPackTest {
clientWant.writeString("done\n");
try (ByteArrayOutputStream serverResponse = new ByteArrayOutputStream()) {
-
uploadPack.setPreUploadHook(new PreUploadHook() {
@Override
public void onBeginNegotiateRound(UploadPack up,
@@ -2952,6 +3007,7 @@ public class UploadPackTest {
assertTrue(objDb.has(one.toObjectId()));
}
}
+}
@Test
public void testSafeToClearRefsInFetchV0() throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
index e463e9070a..7b9e70d4da 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
@@ -25,8 +25,9 @@ import java.time.Instant;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.dircache.Checkout;
import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheCheckout;
+import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
import org.eclipse.jgit.dircache.DirCacheEntry;
@@ -38,6 +39,7 @@ import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -303,11 +305,12 @@ public class FileTreeIteratorTest extends RepositoryTestCase {
DirCacheEntry dce = db.readDirCache().getEntry("symlink");
dce.setFileMode(FileMode.SYMLINK);
try (ObjectReader objectReader = db.newObjectReader()) {
+ Checkout checkout = new Checkout(db).setRecursiveDeletion(false);
+ checkout.checkout(dce,
+ new CheckoutMetadata(EolStreamType.DIRECT, null),
+ objectReader, null);
WorkingTreeOptions options = db.getConfig()
.get(WorkingTreeOptions.KEY);
- DirCacheCheckout.checkoutEntry(db, dce, objectReader, false, null,
- options);
-
FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(),
options);
while (!fti.getEntryPathString().equals("symlink")) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
index 32652494d2..44e8632228 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
@@ -12,7 +12,9 @@ package org.eclipse.jgit.util;
import static org.junit.Assert.assertEquals;
-import java.util.concurrent.TimeUnit;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZoneId;
import org.eclipse.jgit.junit.MockSystemReader;
import org.eclipse.jgit.lib.ObjectId;
@@ -53,9 +55,9 @@ public class ChangeIdUtilTest {
MockSystemReader mockSystemReader = new MockSystemReader();
- final long when = mockSystemReader.getCurrentTime();
+ Instant when = mockSystemReader.now();
- final int tz = new MockSystemReader().getTimezone(when);
+ ZoneId tz = new MockSystemReader().getTimeZoneAt(when);
PersonIdent author = new PersonIdent("J. Author", "ja@example.com");
{
@@ -218,23 +220,23 @@ public class ChangeIdUtilTest {
@Test
public void testACommitWithSubjectBodyBugBrackersAndSob() throws Exception {
assertEquals(
- "a commit with subject body, bug. brackers and sob\n\nText\n\nBug: 33\nChange-Id: I90ecb589bef766302532c3e00915e10114b00f62\n[bracket]\nSigned-off-by: me@you.too\n",
- call("a commit with subject body, bug. brackers and sob\n\nText\n\nBug: 33\n[bracket]\nSigned-off-by: me@you.too\n\n"));
+ "a commit with subject body, bug, brackers and sob\n\nText\n\nBug: 33\n[bracket]\nChange-Id: I94dc6ed919a4baaa7c1bf8712717b888c6b90363\nSigned-off-by: me@you.too\n",
+ call("a commit with subject body, bug, brackers and sob\n\nText\n\nBug: 33\n[bracket]\nSigned-off-by: me@you.too\n\n"));
}
@Test
public void testACommitWithSubjectBodyBugLineWithASpaceAndSob()
throws Exception {
assertEquals(
- "a commit with subject body, bug. line with a space and sob\n\nText\n\nBug: 33\nChange-Id: I864e2218bdee033c8ce9a7f923af9e0d5dc16863\n \nSigned-off-by: me@you.too\n",
- call("a commit with subject body, bug. line with a space and sob\n\nText\n\nBug: 33\n \nSigned-off-by: me@you.too\n\n"));
+ "a commit with subject body, bug, line with a space and sob\n\nText\n\nBug: 33\n \nChange-Id: I126b472d2e0e64ad8187d61857f0169f9ccdae86\nSigned-off-by: me@you.too\n",
+ call("a commit with subject body, bug, line with a space and sob\n\nText\n\nBug: 33\n \nSigned-off-by: me@you.too\n\n"));
}
@Test
public void testACommitWithSubjectBodyBugEmptyLineAndSob() throws Exception {
assertEquals(
- "a commit with subject body, bug. empty line and sob\n\nText\n\nBug: 33\nChange-Id: I33f119f533313883e6ada3df600c4f0d4db23a76\n \nSigned-off-by: me@you.too\n",
- call("a commit with subject body, bug. empty line and sob\n\nText\n\nBug: 33\n \nSigned-off-by: me@you.too\n\n"));
+ "a commit with subject body, bug, empty line and sob\n\nText\n\nBug: 33\n\nChange-Id: Ic3b61b6e39a0815669b65302e9e75e6a5a019a26\nSigned-off-by: me@you.too\n",
+ call("a commit with subject body, bug, empty line and sob\n\nText\n\nBug: 33\n\nSigned-off-by: me@you.too\n\n"));
}
@Test
@@ -342,9 +344,7 @@ public class ChangeIdUtilTest {
/** Increment the {@link #author} and {@link #committer} times. */
protected void tick() {
- final long delta = TimeUnit.MILLISECONDS.convert(5 * 60,
- TimeUnit.SECONDS);
- final long now = author.getWhen().getTime() + delta;
+ Instant now = author.getWhenAsInstant().plus(Duration.ofMinutes(5));
author = new PersonIdent(author, now, tz);
committer = new PersonIdent(committer, now, tz);
@@ -528,7 +528,7 @@ public class ChangeIdUtilTest {
}
@Test
- public void testChangeIdAfterBugOrIssue() throws Exception {
+ public void testChangeIdAfterOtherFooters() throws Exception {
assertEquals("a\n" + //
"\n" + //
"Bug: 42\n" + //
@@ -541,6 +541,18 @@ public class ChangeIdUtilTest {
assertEquals("a\n" + //
"\n" + //
+ "Bug: 42\n" + //
+ " multi-line Bug footer\n" + //
+ "Change-Id: Icc953ef35f1a4ee5eb945132aefd603ae3d9dd9f\n" + //
+ SOB1,//
+ call("a\n" + //
+ "\n" + //
+ "Bug: 42\n" + //
+ " multi-line Bug footer\n" + //
+ SOB1));
+
+ assertEquals("a\n" + //
+ "\n" + //
"Issue: 42\n" + //
"Change-Id: Ie66e07d89ae5b114c0975b49cf326e90331dd822\n" + //
SOB1,//
@@ -548,6 +560,14 @@ public class ChangeIdUtilTest {
"\n" + //
"Issue: 42\n" + //
SOB1));
+
+ assertEquals("a\n" + //
+ "\n" + //
+ "Other: none\n" + //
+ "Change-Id: Ide70e625dea61854206378a377dd12e462ae720f\n",//
+ call("a\n" + //
+ "\n" + //
+ "Other: none\n"));
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateFormatterTest.java
index 6a531fe0e6..7ef386f6ee 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateFormatterTest.java
@@ -89,7 +89,6 @@ public class GitDateFormatterTest {
@Test
public void LOCALE() {
String date = new GitDateFormatter(Format.LOCALE).formatDate(ident);
- System.out.println(date);
assertTrue("Sep 20, 2011 7:09:25 PM -0400".equals(date)
|| "Sep 20, 2011, 7:09:25 PM -0400".equals(date) // JDK-8206961
|| "Sep 20, 2011, 7:09:25\u202FPM -0400".equals(date)); // JDK-8304925
@@ -99,7 +98,6 @@ public class GitDateFormatterTest {
public void LOCALELOCAL() {
String date = new GitDateFormatter(Format.LOCALELOCAL)
.formatDate(ident);
- System.out.println(date);
assertTrue("Sep 20, 2011 7:39:25 PM".equals(date)
|| "Sep 20, 2011, 7:39:25 PM".equals(date) // JDK-8206961
|| "Sep 20, 2011, 7:39:25\u202FPM".equals(date)); // JDK-8304925
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserBadlyFormattedTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserBadlyFormattedTest.java
new file mode 100644
index 0000000000..a59d7bc7bb
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserBadlyFormattedTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick 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.assertThrows;
+
+import java.text.ParseException;
+
+import org.eclipse.jgit.junit.MockSystemReader;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests which assert that unparseable Strings lead to ParseExceptions
+ */
+@RunWith(Theories.class)
+public class GitTimeParserBadlyFormattedTest {
+ private String dateStr;
+
+ @Before
+ public void setUp() {
+ MockSystemReader mockSystemReader = new MockSystemReader();
+ SystemReader.setInstance(mockSystemReader);
+ }
+
+ @After
+ public void tearDown() {
+ SystemReader.setInstance(null);
+ }
+
+ public GitTimeParserBadlyFormattedTest(String dateStr) {
+ this.dateStr = dateStr;
+ }
+
+ @DataPoints
+ public static String[] getDataPoints() {
+ return new String[] { "", ".", "...", "1970", "3000.3000.3000", "3 yesterday ago",
+ "now yesterday ago", "yesterdays", "3.day. 2.week.ago",
+ "day ago", "Gra Feb 21 15:35:00 2007 +0100",
+ "Sun Feb 21 15:35:00 2007 +0100",
+ "Wed Feb 21 15:35:00 Grand +0100" };
+ }
+
+ @Theory
+ public void badlyFormattedWithoutRef() {
+ assertThrows(
+ "The expected ParseException while parsing '" + dateStr
+ + "' did not occur.",
+ ParseException.class, () -> GitTimeParser.parse(dateStr));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserTest.java
new file mode 100644
index 0000000000..0e5eb283a4
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserTest.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2024, Christian Halstrick 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.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.text.ParseException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.Period;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+
+import org.eclipse.jgit.junit.MockSystemReader;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GitTimeParserTest {
+ MockSystemReader mockSystemReader;
+
+ @Before
+ public void setUp() {
+ mockSystemReader = new MockSystemReader();
+ SystemReader.setInstance(mockSystemReader);
+ }
+
+ @After
+ public void tearDown() {
+ SystemReader.setInstance(null);
+ }
+
+ @Test
+ public void yesterday() throws ParseException {
+ LocalDateTime parse = GitTimeParser.parse("yesterday");
+
+ LocalDateTime now = SystemReader.getInstance().civilNow();
+ assertEquals(Period.between(parse.toLocalDate(), now.toLocalDate()),
+ Period.ofDays(1));
+ }
+
+ @Test
+ public void never() throws ParseException {
+ LocalDateTime parse = GitTimeParser.parse("never");
+ assertEquals(LocalDateTime.MAX, parse);
+ }
+
+ @Test
+ public void now_pointInTime() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 15:35:00 +0100");
+
+ LocalDateTime parsedNow = GitTimeParser.parse("now", aTime);
+
+ assertEquals(aTime, parsedNow);
+ }
+
+ @Test
+ public void now_systemTime() throws ParseException {
+ LocalDateTime firstNow = GitTimeParser.parse("now");
+ assertEquals(SystemReader.getInstance().civilNow(), firstNow);
+ mockSystemReader.tick(10);
+ LocalDateTime secondNow = GitTimeParser.parse("now");
+ assertTrue(secondNow.isAfter(firstNow));
+ }
+
+ @Test
+ public void weeksAgo() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 15:35:00 +0100");
+
+ LocalDateTime parse = GitTimeParser.parse("2 weeks ago", aTime);
+ assertEquals(asLocalDateTime("2007-02-07 15:35:00 +0100"), parse);
+ }
+
+ @Test
+ public void daysAndWeeksAgo() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 15:35:00 +0100");
+
+ LocalDateTime twoWeeksAgoActual = GitTimeParser.parse("2 weeks ago",
+ aTime);
+
+ LocalDateTime twoWeeksAgoExpected = asLocalDateTime(
+ "2007-02-07 15:35:00 +0100");
+ assertEquals(twoWeeksAgoExpected, twoWeeksAgoActual);
+
+ LocalDateTime combinedWhitespace = GitTimeParser
+ .parse("3 days 2 weeks ago", aTime);
+ LocalDateTime combinedWhitespaceExpected = asLocalDateTime(
+ "2007-02-04 15:35:00 +0100");
+ assertEquals(combinedWhitespaceExpected, combinedWhitespace);
+
+ LocalDateTime combinedDots = GitTimeParser.parse("3.day.2.week.ago",
+ aTime);
+ LocalDateTime combinedDotsExpected = asLocalDateTime(
+ "2007-02-04 15:35:00 +0100");
+ assertEquals(combinedDotsExpected, combinedDots);
+ }
+
+ @Test
+ public void hoursAgo() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 17:35:00 +0100");
+
+ LocalDateTime twoHoursAgoActual = GitTimeParser.parse("2 hours ago",
+ aTime);
+
+ LocalDateTime twoHoursAgoExpected = asLocalDateTime(
+ "2007-02-21 15:35:00 +0100");
+ assertEquals(twoHoursAgoExpected, twoHoursAgoActual);
+ }
+
+ @Test
+ public void hoursAgo_acrossDay() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 00:35:00 +0100");
+
+ LocalDateTime twoHoursAgoActual = GitTimeParser.parse("2 hours ago",
+ aTime);
+
+ LocalDateTime twoHoursAgoExpected = asLocalDateTime(
+ "2007-02-20 22:35:00 +0100");
+ assertEquals(twoHoursAgoExpected, twoHoursAgoActual);
+ }
+
+ @Test
+ public void minutesHoursAgoCombined() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-04 15:35:00 +0100");
+
+ LocalDateTime combinedWhitespace = GitTimeParser
+ .parse("3 hours 2 minutes ago", aTime);
+ LocalDateTime combinedWhitespaceExpected = asLocalDateTime(
+ "2007-02-04 12:33:00 +0100");
+ assertEquals(combinedWhitespaceExpected, combinedWhitespace);
+
+ LocalDateTime combinedDots = GitTimeParser
+ .parse("3.hours.2.minutes.ago", aTime);
+ LocalDateTime combinedDotsExpected = asLocalDateTime(
+ "2007-02-04 12:33:00 +0100");
+ assertEquals(combinedDotsExpected, combinedDots);
+ }
+
+ @Test
+ public void minutesAgo() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 17:35:10 +0100");
+
+ LocalDateTime twoMinutesAgo = GitTimeParser.parse("2 minutes ago",
+ aTime);
+
+ LocalDateTime twoMinutesAgoExpected = asLocalDateTime(
+ "2007-02-21 17:33:10 +0100");
+ assertEquals(twoMinutesAgoExpected, twoMinutesAgo);
+ }
+
+ @Test
+ public void minutesAgo_acrossDay() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 00:35:10 +0100");
+
+ LocalDateTime minutesAgoActual = GitTimeParser.parse("40 minutes ago",
+ aTime);
+
+ LocalDateTime minutesAgoExpected = asLocalDateTime(
+ "2007-02-20 23:55:10 +0100");
+ assertEquals(minutesAgoExpected, minutesAgoActual);
+ }
+
+ @Test
+ public void iso() throws ParseException {
+ String dateStr = "2007-02-21 15:35:00 +0100";
+
+ LocalDateTime actual = GitTimeParser.parse(dateStr);
+
+ LocalDateTime expected = asLocalDateTime(dateStr);
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void rfc() throws ParseException {
+ String dateStr = "Wed, 21 Feb 2007 15:35:00 +0100";
+
+ LocalDateTime actual = GitTimeParser.parse(dateStr);
+
+ LocalDateTime expected = asLocalDateTime(dateStr,
+ "EEE, dd MMM yyyy HH:mm:ss Z");
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void shortFmt() throws ParseException {
+ assertParsing("2007-02-21", "yyyy-MM-dd");
+ }
+
+ @Test
+ public void shortWithDots() throws ParseException {
+ assertParsing("2007.02.21", "yyyy.MM.dd");
+ }
+
+ @Test
+ public void shortWithSlash() throws ParseException {
+ assertParsing("02/21/2007", "MM/dd/yyyy");
+ }
+
+ @Test
+ public void shortWithDotsReverse() throws ParseException {
+ assertParsing("21.02.2007", "dd.MM.yyyy");
+ }
+
+ @Test
+ public void defaultFmt() throws ParseException {
+ assertParsing("Wed Feb 21 15:35:00 2007 +0100",
+ "EEE MMM dd HH:mm:ss yyyy Z");
+ }
+
+ @Test
+ public void local() throws ParseException {
+ assertParsing("Wed Feb 21 15:35:00 2007", "EEE MMM dd HH:mm:ss yyyy");
+ }
+
+ private static void assertParsing(String dateStr, String format)
+ throws ParseException {
+ LocalDateTime actual = GitTimeParser.parse(dateStr);
+
+ LocalDateTime expected = asLocalDateTime(dateStr, format);
+ assertEquals(expected, actual);
+ }
+
+ private static LocalDateTime asLocalDateTime(String dateStr) {
+ return asLocalDateTime(dateStr, "yyyy-MM-dd HH:mm:ss Z");
+ }
+
+ private static LocalDateTime asLocalDateTime(String dateStr,
+ String pattern) {
+ DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattern);
+ TemporalAccessor ta = fmt
+ .withZone(SystemReader.getInstance().getTimeZoneId())
+ .withLocale(SystemReader.getInstance().getLocale())
+ .parse(dateStr);
+ return ta.isSupported(ChronoField.HOUR_OF_DAY) ? LocalDateTime.from(ta)
+ : LocalDate.from(ta).atStartOfDay();
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_ParsePersonIdentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_ParsePersonIdentTest.java
index 355bbbab16..6d23db81d8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_ParsePersonIdentTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_ParsePersonIdentTest.java
@@ -10,10 +10,13 @@
package org.eclipse.jgit.util;
+import static java.time.Instant.EPOCH;
+import static java.time.ZoneOffset.UTC;
import static org.junit.Assert.assertEquals;
-import java.util.Date;
-import java.util.TimeZone;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import org.eclipse.jgit.lib.PersonIdent;
import org.junit.Test;
@@ -22,8 +25,8 @@ public class RawParseUtils_ParsePersonIdentTest {
@Test
public void testParsePersonIdent_legalCases() {
- final Date when = new Date(1234567890000L);
- final TimeZone tz = TimeZone.getTimeZone("GMT-7");
+ Instant when = Instant.ofEpochMilli(1234567890000L);
+ ZoneId tz = ZoneOffset.ofHours(-7);
assertPersonIdent("Me <me@example.com> 1234567890 -0700",
new PersonIdent("Me", "me@example.com", when, tz));
@@ -50,8 +53,8 @@ public class RawParseUtils_ParsePersonIdentTest {
@Test
public void testParsePersonIdent_fuzzyCases() {
- final Date when = new Date(1234567890000L);
- final TimeZone tz = TimeZone.getTimeZone("GMT-7");
+ Instant when = Instant.ofEpochMilli(1234567890000L);
+ ZoneId tz = ZoneOffset.ofHours(-7);
assertPersonIdent(
"A U Thor <author@example.com>, C O. Miter <comiter@example.com> 1234567890 -0700",
@@ -64,8 +67,8 @@ public class RawParseUtils_ParsePersonIdentTest {
@Test
public void testParsePersonIdent_incompleteCases() {
- final Date when = new Date(1234567890000L);
- final TimeZone tz = TimeZone.getTimeZone("GMT-7");
+ Instant when = Instant.ofEpochMilli(1234567890000L);
+ ZoneId tz = ZoneOffset.ofHours(-7);
assertPersonIdent("Me <> 1234567890 -0700", new PersonIdent("Me", "",
when, tz));
@@ -76,26 +79,26 @@ public class RawParseUtils_ParsePersonIdentTest {
assertPersonIdent(" <> 1234567890 -0700", new PersonIdent("", "", when,
tz));
- assertPersonIdent("<>", new PersonIdent("", "", 0, 0));
+ assertPersonIdent("<>", new PersonIdent("", "", EPOCH, UTC));
- assertPersonIdent(" <>", new PersonIdent("", "", 0, 0));
+ assertPersonIdent(" <>", new PersonIdent("", "", EPOCH, UTC));
assertPersonIdent("<me@example.com>", new PersonIdent("",
- "me@example.com", 0, 0));
+ "me@example.com", EPOCH, UTC));
assertPersonIdent(" <me@example.com>", new PersonIdent("",
- "me@example.com", 0, 0));
+ "me@example.com", EPOCH, UTC));
- assertPersonIdent("Me <>", new PersonIdent("Me", "", 0, 0));
+ assertPersonIdent("Me <>", new PersonIdent("Me", "", EPOCH, UTC));
assertPersonIdent("Me <me@example.com>", new PersonIdent("Me",
- "me@example.com", 0, 0));
+ "me@example.com", EPOCH, UTC));
assertPersonIdent("Me <me@example.com> 1234567890", new PersonIdent(
- "Me", "me@example.com", 0, 0));
+ "Me", "me@example.com", EPOCH, UTC));
assertPersonIdent("Me <me@example.com> 1234567890 ", new PersonIdent(
- "Me", "me@example.com", 0, 0));
+ "Me", "me@example.com", EPOCH, UTC));
}
@Test
@@ -104,6 +107,21 @@ public class RawParseUtils_ParsePersonIdentTest {
assertPersonIdent("Me <me@example.com 1234567890 -0700", null);
}
+ @Test
+ public void testParsePersonIdent_badTz() {
+ PersonIdent tooBig = RawParseUtils
+ .parsePersonIdent("Me <me@example.com> 1234567890 +8315");
+ assertEquals(tooBig.getZoneOffset().getTotalSeconds(), 0);
+
+ PersonIdent tooSmall = RawParseUtils
+ .parsePersonIdent("Me <me@example.com> 1234567890 -8315");
+ assertEquals(tooSmall.getZoneOffset().getTotalSeconds(), 0);
+
+ PersonIdent notATime = RawParseUtils
+ .parsePersonIdent("Me <me@example.com> 1234567890 -0370");
+ assertEquals(notATime.getZoneOffset().getTotalSeconds(), 0);
+ }
+
private static void assertPersonIdent(String line, PersonIdent expected) {
PersonIdent actual = RawParseUtils.parsePersonIdent(line);
assertEquals(expected, actual);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java
index 214bbca944..a927d8dbef 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java
@@ -16,7 +16,7 @@ import static org.eclipse.jgit.util.RelativeDateFormatter.SECOND_IN_MILLIS;
import static org.eclipse.jgit.util.RelativeDateFormatter.YEAR_IN_MILLIS;
import static org.junit.Assert.assertEquals;
-import java.util.Date;
+import java.time.Instant;
import org.eclipse.jgit.junit.MockSystemReader;
import org.junit.After;
@@ -37,9 +37,9 @@ public class RelativeDateFormatterTest {
private static void assertFormat(long ageFromNow, long timeUnit,
String expectedFormat) {
- Date d = new Date(SystemReader.getInstance().getCurrentTime()
- - ageFromNow * timeUnit);
- String s = RelativeDateFormatter.format(d);
+ long millis = ageFromNow * timeUnit;
+ Instant aTime = SystemReader.getInstance().now().minusMillis(millis);
+ String s = RelativeDateFormatter.format(aTime);
assertEquals(expectedFormat, s);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java
index 015da164c3..9a1c710752 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java
@@ -12,6 +12,7 @@ package org.eclipse.jgit.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -172,4 +173,22 @@ public class StringUtilsTest {
assertEquals("foo bar ",
StringUtils.commonPrefix("foo bar 42", "foo bar 24"));
}
+
+ @Test
+ public void testTrim() {
+ assertEquals("a", StringUtils.trim("a", '/'));
+ assertEquals("aaaa", StringUtils.trim("aaaa", '/'));
+ assertEquals("aaa", StringUtils.trim("/aaa", '/'));
+ assertEquals("aaa", StringUtils.trim("aaa/", '/'));
+ assertEquals("aaa", StringUtils.trim("/aaa/", '/'));
+ assertEquals("aa/aa", StringUtils.trim("/aa/aa/", '/'));
+ assertEquals("aa/aa", StringUtils.trim("aa/aa", '/'));
+
+ assertEquals("", StringUtils.trim("", '/'));
+ assertEquals("", StringUtils.trim("/", '/'));
+ assertEquals("", StringUtils.trim("//", '/'));
+ assertEquals("", StringUtils.trim("///", '/'));
+
+ assertNull(StringUtils.trim(null, '/'));
+ }
}
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index 04bda44748..e0b049c521 100644
--- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -4,14 +4,14 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.ui
Bundle-SymbolicName: org.eclipse.jgit.ui
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Export-Package: org.eclipse.jgit.awtui;version="7.0.0"
-Import-Package: org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revplot;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)"
+Export-Package: org.eclipse.jgit.awtui;version="7.3.0"
+Import-Package: org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revplot;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)"
diff --git a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
index 8170fc6bd2..66617ca8db 100644
--- a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.ui - Sources
Bundle-SymbolicName: org.eclipse.jgit.ui.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ui;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ui;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index 6d1e2676af..3d8d2a1b41 100644
--- a/org.eclipse.jgit.ui/pom.xml
+++ b/org.eclipse.jgit.ui/pom.xml
@@ -19,7 +19,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ui</artifactId>
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index caefce39dc..877a488a73 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -1,10 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.jgit" version="2">
- <resource path="src/org/eclipse/jgit/lib/GpgSignatureVerifier.java" type="org.eclipse.jgit.lib.GpgSignatureVerifier">
- <filter id="404000815">
+ <resource path="src/org/eclipse/jgit/lib/RefDatabase.java" type="org.eclipse.jgit.lib.RefDatabase">
+ <filter id="336695337">
<message_arguments>
- <message_argument value="org.eclipse.jgit.lib.GpgSignatureVerifier"/>
- <message_argument value="verify(GpgConfig, byte[], byte[])"/>
+ <message_argument value="org.eclipse.jgit.lib.RefDatabase"/>
+ <message_argument value="getReflogReader(Ref)"/>
+ </message_arguments>
+ </filter>
+ <filter id="336695337">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.RefDatabase"/>
+ <message_argument value="getReflogReader(String)"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/lib/TypedConfigGetter.java" type="org.eclipse.jgit.lib.TypedConfigGetter">
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+ <message_argument value="getBoolean(Config, String, String, String, Boolean)"/>
+ </message_arguments>
+ </filter>
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+ <message_argument value="getInt(Config, String, String, String, Integer)"/>
+ </message_arguments>
+ </filter>
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+ <message_argument value="getIntInRange(Config, String, String, String, Integer, Integer, Integer)"/>
+ </message_arguments>
+ </filter>
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+ <message_argument value="getIntInRange(Config, String, String, String, int, int, Integer)"/>
+ </message_arguments>
+ </filter>
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+ <message_argument value="getLong(Config, String, String, String, Long)"/>
+ </message_arguments>
+ </filter>
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+ <message_argument value="getTimeUnit(Config, String, String, String, Long, TimeUnit)"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/revwalk/RevWalk.java" type="org.eclipse.jgit.revwalk.RevWalk">
+ <filter id="1142947843">
+ <message_arguments>
+ <message_argument value="6.10.1"/>
+ <message_argument value="isMergedIntoAnyCommit(RevCommit, Collection&lt;RevCommit&gt;)"/>
</message_arguments>
</filter>
</resource>
diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
index c4dc76fe34..ef3d8ecaba 100644
--- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
@@ -40,7 +40,7 @@ org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 2cdfa994bb..5f851ec541 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit
Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
Service-Component: OSGI-INF/org.eclipse.jgit.internal.util.CleanupService.xml
Eclipse-ExtensibleAPI: true
-Export-Package: org.eclipse.jgit.annotations;version="7.0.0",
- org.eclipse.jgit.api;version="7.0.0";
+Export-Package: org.eclipse.jgit.annotations;version="7.3.0",
+ org.eclipse.jgit.api;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.notes,
org.eclipse.jgit.dircache,
@@ -25,72 +25,77 @@ Export-Package: org.eclipse.jgit.annotations;version="7.0.0",
org.eclipse.jgit.revwalk.filter,
org.eclipse.jgit.blame,
org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="7.0.0";
+ org.eclipse.jgit.api.errors;version="7.3.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="7.0.0";
+ org.eclipse.jgit.attributes;version="7.3.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk",
- org.eclipse.jgit.blame;version="7.0.0";
+ org.eclipse.jgit.blame;version="7.3.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
- org.eclipse.jgit.treewalk.filter,
- org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="7.0.0";
+ org.eclipse.jgit.blame.cache,
+ org.eclipse.jgit.diff,
+ org.eclipse.jgit.treewalk.filter",
+ org.eclipse.jgit.blame.cache;version="7.3.0";
+ uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.diff;version="7.3.0";
uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.attributes,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.patch,
+ org.eclipse.jgit.attributes,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="7.0.0";
+ org.eclipse.jgit.dircache;version="7.3.0";
uses:="org.eclipse.jgit.events,
org.eclipse.jgit.lib,
org.eclipse.jgit.attributes,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util",
- org.eclipse.jgit.errors;version="7.0.0";
+ org.eclipse.jgit.errors;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.dircache,
- org.eclipse.jgit.lib,
- org.eclipse.jgit.internal.storage.pack",
- org.eclipse.jgit.events;version="7.0.0";
+ org.eclipse.jgit.lib",
+ org.eclipse.jgit.events;version="7.3.0";
uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="7.0.0",
- org.eclipse.jgit.gitrepo;version="7.0.0";
+ org.eclipse.jgit.fnmatch;version="7.3.0",
+ org.eclipse.jgit.gitrepo;version="7.3.0";
uses:="org.xml.sax.helpers,
org.eclipse.jgit.api,
+ org.eclipse.jgit.api.errors,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.xml.sax",
- org.eclipse.jgit.gitrepo.internal;version="7.0.0";x-internal:=true,
- org.eclipse.jgit.hooks;version="7.0.0";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="7.0.0",
- org.eclipse.jgit.ignore.internal;version="7.0.0";
+ org.eclipse.jgit.gitrepo.internal;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.hooks;version="7.3.0";
+ uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.util",
+ org.eclipse.jgit.ignore;version="7.3.0",
+ org.eclipse.jgit.ignore.internal;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="7.0.0";
+ org.eclipse.jgit.internal;version="7.3.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.diff;version="7.0.0";
+ org.eclipse.jgit.internal.diff;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.diffmergetool;version="7.0.0";
+ org.eclipse.jgit.internal.diffmergetool;version="7.3.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.pgm.test,
org.eclipse.jgit.pgm,
org.eclipse.egit.ui",
- org.eclipse.jgit.internal.fsck;version="7.0.0";
+ org.eclipse.jgit.internal.fsck;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.revwalk;version="7.0.0";
+ org.eclipse.jgit.internal.revwalk;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.storage.commitgraph;version="7.0.0";
+ org.eclipse.jgit.internal.storage.commitgraph;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.storage.dfs;version="7.0.0";
+ org.eclipse.jgit.internal.storage.dfs;version="7.3.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.http.server,
org.eclipse.jgit.http.test,
org.eclipse.jgit.lfs.test",
- org.eclipse.jgit.internal.storage.file;version="7.0.0";
+ org.eclipse.jgit.internal.storage.file;version="7.3.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.junit,
org.eclipse.jgit.junit.http,
@@ -99,41 +104,43 @@ Export-Package: org.eclipse.jgit.annotations;version="7.0.0",
org.eclipse.jgit.pgm,
org.eclipse.jgit.pgm.test,
org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.internal.storage.io;version="7.0.0";
+ org.eclipse.jgit.internal.storage.io;version="7.3.0";
x-friends:="org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.memory;version="7.0.0";
+ org.eclipse.jgit.internal.storage.memory;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.storage.pack;version="7.0.0";
+ org.eclipse.jgit.internal.storage.midx;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.internal.storage.pack;version="7.3.0";
x-friends:="org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftable;version="7.0.0";
+ org.eclipse.jgit.internal.storage.reftable;version="7.3.0";
x-friends:="org.eclipse.jgit.http.test,
org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.submodule;version="7.0.0";x-internal:=true,
- org.eclipse.jgit.internal.transport.connectivity;version="7.0.0";
+ org.eclipse.jgit.internal.submodule;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.connectivity;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.http;version="7.0.0";
+ org.eclipse.jgit.internal.transport.http;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.parser;version="7.0.0";
+ org.eclipse.jgit.internal.transport.parser;version="7.3.0";
x-friends:="org.eclipse.jgit.http.server,
org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.ssh;version="7.0.0";
+ org.eclipse.jgit.internal.transport.ssh;version="7.3.0";
x-friends:="org.eclipse.jgit.ssh.apache,
org.eclipse.jgit.ssh.jsch,
org.eclipse.jgit.test",
- org.eclipse.jgit.internal.util;version="7.0.0";
- x-friends:=" org.eclipse.jgit.junit",
- org.eclipse.jgit.lib;version="7.0.0";
+ org.eclipse.jgit.internal.util;version="7.3.0";
+ x-friends:="org.eclipse.jgit.junit",
+ org.eclipse.jgit.lib;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.util.sha1,
org.eclipse.jgit.dircache,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.internal.storage.file,
+ org.eclipse.jgit.api,
org.eclipse.jgit.attributes,
org.eclipse.jgit.events,
com.googlecode.javaewah,
@@ -142,12 +149,12 @@ Export-Package: org.eclipse.jgit.annotations;version="7.0.0",
org.eclipse.jgit.util,
org.eclipse.jgit.submodule,
org.eclipse.jgit.util.time",
- org.eclipse.jgit.lib.internal;version="7.0.0";
+ org.eclipse.jgit.lib.internal;version="7.3.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.pgm,
org.eclipse.egit.ui",
- org.eclipse.jgit.logging;version="7.0.0",
- org.eclipse.jgit.merge;version="7.0.0";
+ org.eclipse.jgit.logging;version="7.3.0",
+ org.eclipse.jgit.merge;version="7.3.0";
uses:="org.eclipse.jgit.dircache,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
@@ -156,67 +163,69 @@ Export-Package: org.eclipse.jgit.annotations;version="7.0.0",
org.eclipse.jgit.util,
org.eclipse.jgit.api,
org.eclipse.jgit.attributes",
- org.eclipse.jgit.nls;version="7.0.0",
- org.eclipse.jgit.notes;version="7.0.0";
+ org.eclipse.jgit.nls;version="7.3.0",
+ org.eclipse.jgit.notes;version="7.3.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="7.0.0";
+ org.eclipse.jgit.patch;version="7.3.0";
uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.revwalk,
org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="7.0.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="7.0.0";
+ org.eclipse.jgit.revplot;version="7.3.0";
+ uses:="org.eclipse.jgit.revwalk,
+ org.eclipse.jgit.lib",
+ org.eclipse.jgit.revwalk;version="7.3.0";
uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.revwalk.filter,
org.eclipse.jgit.diff,
org.eclipse.jgit.treewalk.filter,
- org.eclipse.jgit.revwalk.filter,
- org.eclipse.jgit.treewalk",
- org.eclipse.jgit.revwalk.filter;version="7.0.0";
+ org.eclipse.jgit.treewalk,
+ org.eclipse.jgit.internal.storage.commitgraph",
+ org.eclipse.jgit.revwalk.filter;version="7.3.0";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.lib,
org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="7.0.0";
+ org.eclipse.jgit.storage.file;version="7.3.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="7.0.0";
+ org.eclipse.jgit.storage.pack;version="7.3.0";
uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="7.0.0";
+ org.eclipse.jgit.submodule;version="7.3.0";
uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.diff,
org.eclipse.jgit.treewalk.filter,
+ org.eclipse.jgit.diff,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util",
- org.eclipse.jgit.transport;version="7.0.0";
+ org.eclipse.jgit.transport;version="7.3.0";
uses:="javax.crypto,
+ org.eclipse.jgit.hooks,
org.eclipse.jgit.util.io,
org.eclipse.jgit.lib,
- org.eclipse.jgit.revwalk,
org.eclipse.jgit.transport.http,
- org.eclipse.jgit.internal.storage.file,
+ org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util,
org.eclipse.jgit.internal.storage.pack,
org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.storage.pack,
org.eclipse.jgit.errors",
- org.eclipse.jgit.transport.http;version="7.0.0";
+ org.eclipse.jgit.transport.http;version="7.3.0";
uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="7.0.0";
+ org.eclipse.jgit.transport.resolver;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.lib",
- org.eclipse.jgit.treewalk;version="7.0.0";
+ org.eclipse.jgit.treewalk;version="7.3.0";
uses:="org.eclipse.jgit.dircache,
org.eclipse.jgit.lib,
org.eclipse.jgit.attributes,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util",
- org.eclipse.jgit.treewalk.filter;version="7.0.0";
+ org.eclipse.jgit.treewalk.filter;version="7.3.0";
uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="7.0.0";
+ org.eclipse.jgit.util;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.hooks,
org.eclipse.jgit.revwalk,
@@ -229,18 +238,18 @@ Export-Package: org.eclipse.jgit.annotations;version="7.0.0",
org.eclipse.jgit.treewalk,
javax.net.ssl,
org.eclipse.jgit.util.time",
- org.eclipse.jgit.util.io;version="7.0.0";
+ org.eclipse.jgit.util.io;version="7.3.0";
uses:="org.eclipse.jgit.attributes,
org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util.sha1;version="7.0.0",
- org.eclipse.jgit.util.time;version="7.0.0"
+ org.eclipse.jgit.util.sha1;version="7.3.0",
+ org.eclipse.jgit.util.time;version="7.3.0"
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
javax.crypto,
javax.management,
javax.net.ssl,
- org.apache.commons.codec.digest;version="1.15.0",
+ org.apache.commons.codec.digest;version="[1.15.0,2.0.0)",
org.slf4j;version="[1.7.0,3.0.0)",
org.xml.sax,
org.xml.sax.helpers
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index 7531018bc3..a9e669b3f7 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit - Sources
Bundle-SymbolicName: org.eclipse.jgit.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index 939767486e..c65a440cc9 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit/pom.xml
@@ -20,7 +20,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit</artifactId>
@@ -49,7 +49,6 @@
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
</dependency>
</dependencies>
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 19c90086aa..27270a1f25 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -64,8 +64,6 @@ binaryHunkDecodeError=Binary hunk, line {0}: invalid input
binaryHunkInvalidLength=Binary hunk, line {0}: input corrupt; expected length byte, got 0x{1}
binaryHunkLineTooShort=Binary hunk, line {0}: input ended prematurely
binaryHunkMissingNewline=Binary hunk, line {0}: input line not terminated by newline
-bitmapAccessErrorForPackfile=Error whilst trying to access bitmap file for {}
-bitmapFailedToGet=Failed to get bitmap index file {}
bitmapMissingObject=Bitmap at {0} is missing {1}.
bitmapsMustBePrepared=Bitmaps must be prepared before they may be written.
bitmapUseNoopNoListener=Use NOOP instance for no listener
@@ -78,6 +76,7 @@ branchNameInvalid=Branch name {0} is not allowed
buildingBitmaps=Building bitmaps
cachedPacksPreventsIndexCreation=Using cached packs prevents index creation
cachedPacksPreventsListingObjects=Using cached packs prevents listing objects
+cacheRegionAllOrNoneNull=expected all null or none: {0}, {1}
cannotAccessLastModifiedForSafeDeletion=Unable to access lastModifiedTime of file {0}, skip deletion since we cannot safely avoid race condition
cannotBeCombined=Cannot be combined.
cannotBeRecursiveWhenTreesAreIncluded=TreeWalk shouldn't be recursive when tree objects are included.
@@ -267,6 +266,7 @@ deletingBranches=Deleting branches...
deletingNotSupported=Deleting {0} not supported.
depthMustBeAt1=Depth must be >= 1
depthWithUnshallow=Depth and unshallow can\'t be used together
+deprecatedTrustFolderStat=Option core.trustFolderStat is deprecated, replace it by core.trustStat.
destinationIsNotAWildcard=Destination is not a wildcard.
detachedHeadDetected=HEAD is detached
diffToolNotGivenError=No diff tool provided and no defaults configured.
@@ -285,6 +285,9 @@ DIRCUnrecognizedExtendedFlags=Unrecognized extended flags: {0}
downloadCancelled=Download cancelled
downloadCancelledDuringIndexing=Download cancelled during indexing
duplicateAdvertisementsOf=duplicate advertisements of {0}
+duplicateCacheTablesGiven=Duplicate cache tables given
+duplicatePackExtensionsForCacheTables=Duplicate pack extension {0} in cache tables
+duplicatePackExtensionsSet=Attempting to configure duplicate pack extensions: {0}.{1}.{2} contains {3}
duplicateRef=Duplicate ref: {0}
duplicateRefAttribute=Duplicate ref attribute: {0}
duplicateRemoteRefUpdateIsIllegal=Duplicate remote ref update is illegal. Affected remote name: {0}
@@ -461,6 +464,7 @@ invalidTimestamp=Invalid timestamp in {0}
invalidTimeUnitValue2=Invalid time unit value: {0}.{1}={2}
invalidTimeUnitValue3=Invalid time unit value: {0}.{1}.{2}={3}
invalidTreeZeroLengthName=Cannot append a tree entry with zero-length name
+invalidTrustStat=core.trustStat must not be set to TrustStat.INHERIT, falling back to TrustStat.ALWAYS.
invalidURL=Invalid URL {0}
invalidWildcards=Invalid wildcards {0}
invalidRefSpec=Invalid refspec {0}
@@ -499,7 +503,7 @@ mergeConflictOnNotes=Merge conflict on note {0}. base = {1}, ours = {2}, theirs
mergeStrategyAlreadyExistsAsDefault=Merge strategy "{0}" already exists as a default strategy
mergeStrategyDoesNotSupportHeads=merge strategy {0} does not support {1} heads to be merged into HEAD
mergeUsingStrategyResultedInDescription=Merge of revisions {0} with base {1} using strategy {2} resulted in: {3}. {4}
-mergeRecursiveConflictsWhenMergingCommonAncestors=Multiple common ancestors were found and merging them resulted in a conflict: {0}, {1}
+mergeRecursiveConflictsWhenMergingCommonAncestors=Multiple common ancestors were found and merging them resulted in a conflict: {0}, {1}\nFailing paths: {2}
mergeRecursiveTooManyMergeBasesFor = "More than {0} merge bases for:\n a {1}\n b {2} found:\n count {3}"
mergeToolNotGivenError=No merge tool provided and no defaults configured.
mergeToolNullError=Parameter for merge tool cannot be null.
@@ -524,6 +528,8 @@ mkDirsFailed=Creating directories for {0} failed
month=month
months=months
monthsAgo={0} months ago
+multiPackIndexUnexpectedSize=MultiPack index: expected %d bytes but out has %d bytes
+multiPackIndexWritingCancelled=Multipack index writing was canceled
multipleMergeBasesFor=Multiple merge bases for:\n {0}\n {1} found:\n {2}\n {3}
nameMustNotBeNullOrEmpty=Ref name must not be null or empty.
need2Arguments=Need 2 arguments
@@ -539,6 +545,8 @@ noMergeBase=No merge base could be determined. Reason={0}. {1}
noMergeHeadSpecified=No merge head specified
nonBareLinkFilesNotSupported=Link files are not supported with nonbare repos
nonCommitToHeads=Cannot point a branch to a non-commit object
+noPackExtConfigurationGiven=No PackExt configuration given
+noPackExtGivenForConfiguration=No PackExt given for configuration
noPathAttributesFound=No Attributes found for {0}.
noSuchRef=no such ref
noSuchRefKnown=no such ref: {0}
@@ -571,7 +579,6 @@ obtainingCommitsForCherryPick=Obtaining commits that need to be cherry-picked
oldIdMustNotBeNull=Expected old ID must not be null
onlyOneFetchSupported=Only one fetch supported
onlyOneOperationCallPerConnectionIsSupported=Only one operation call per connection is supported.
-onlyOpenPgpSupportedForSigning=OpenPGP is the only supported signing option with JGit at this time (gpg.format must be set to openpgp).
openFilesMustBeAtLeast1=Open files must be >= 1
openingConnection=Opening connection
operationCanceled=Operation {0} was canceled
@@ -593,6 +600,8 @@ packInaccessible=Failed to access pack file {0}, caught {1} consecutive errors w
packingCancelledDuringObjectsWriting=Packing cancelled during objects writing
packObjectCountMismatch=Pack object count mismatch: pack {0} index {1}: {2}
packRefs=Pack refs
+packRefsFailed=Packing refs failed
+packRefsSuccessful=Packed refs successfully
packSizeNotSetYet=Pack size not yet set since it has not yet been received
packTooLargeForIndexVersion1=Pack too large for index version 1
packWasDeleted=Pack file {0} was deleted, removing it from pack list
@@ -609,6 +618,7 @@ peerDidNotSupplyACompleteObjectGraph=peer did not supply a complete object graph
personIdentEmailNonNull=E-mail address of PersonIdent must not be null.
personIdentNameNonNull=Name of PersonIdent must not be null.
postCommitHookFailed=Execution of post-commit hook failed: {0}.
+precedenceTrustConfig=Both core.trustFolderStat and core.trustStat are set, ignoring trustFolderStat since trustStat takes precedence. Remove core.trustFolderStat from your configuration.
prefixRemote=remote:
problemWithResolvingPushRefSpecsLocally=Problem with resolving push ref specs locally: {0}
progressMonUploading=Uploading {0}
@@ -636,8 +646,6 @@ readFileStoreAttributesFailed=Reading FileStore attributes from user config fail
readerIsRequired=Reader is required
readingObjectsFromLocalRepositoryFailed=reading objects from local repository failed: {0}
readLastModifiedFailed=Reading lastModified of {0} failed
-readPipeIsNotAllowed=FS.readPipe() isn't allowed for command ''{0}''. Working directory: ''{1}''.
-readPipeIsNotAllowedRequiredPermission=FS.readPipe() isn't allowed for command ''{0}''. Working directory: ''{1}''. Required permission: {2}.
readTimedOut=Read timed out after {0} ms
receivePackObjectTooLarge1=Object too large, rejecting the pack. Max object size limit is {0} bytes.
receivePackObjectTooLarge2=Object too large ({0} bytes), rejecting the pack. Max object size limit is {1} bytes.
@@ -718,6 +726,8 @@ shortSkipOfBlock=Short skip of block.
shutdownCleanup=Cleanup {} during JVM shutdown
shutdownCleanupFailed=Cleanup during JVM shutdown failed
shutdownCleanupListenerFailed=Cleanup of {0} during JVM shutdown failed
+signatureServiceConflict={0} conflict for type {1}. Already registered is {2}; additional factory {3} is ignored.
+signatureTypeUnknown=No signer for {0} signatures. Use another signature type for git config gpg.format, or do not sign.
signatureVerificationError=Signature verification failed
signatureVerificationUnavailable=No signature verifier registered
signedTagMessageNoLf=A non-empty message of a signed tag must end in LF.
@@ -803,6 +813,7 @@ truncatedHunkOldLinesMissing=Truncated hunk, at least {0} old lines is missing
tSizeMustBeGreaterOrEqual1=tSize must be >= 1
unableToCheckConnectivity=Unable to check connectivity.
unableToCreateNewObject=Unable to create new object: {0}
+unableToReadFullArray=Unable to read an array with {0} elements from the stream
unableToReadFullInt=Unable to read a full int from the stream
unableToReadPackfile=Unable to read packfile {0}
unableToRemovePath=Unable to remove path ''{0}''
@@ -829,6 +840,7 @@ unknownObject=unknown object
unknownObjectInIndex=unknown object {0} found in index but not in pack file
unknownObjectType=Unknown object type {0}.
unknownObjectType2=unknown
+unknownPackExtension=Unknown pack extension: {0}.{1}.{2}={3}
unknownPositionEncoding=Unknown position encoding %s
unknownRefStorageFormat=Unknown ref storage format "{0}"
unknownRepositoryFormat=Unknown repository format
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
index c895dc9aaa..b4d1cab513 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
- * Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com> and others
+ * Copyright (C) 2010, 2025 Stefan Lay <stefan.lay@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
@@ -17,6 +17,7 @@ import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
import java.io.IOException;
import java.io.InputStream;
+import java.text.MessageFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -59,8 +60,15 @@ public class AddCommand extends GitCommand<DirCache> {
private WorkingTreeIterator workingTreeIterator;
+ // Update only known index entries, don't add new ones. If there's no file
+ // for an index entry, remove it: stage deletions.
private boolean update = false;
+ // If TRUE, also stage deletions, otherwise only update and add index
+ // entries.
+ // If not set explicitly
+ private Boolean all;
+
// This defaults to true because it's what JGit has been doing
// traditionally. The C git default would be false.
private boolean renormalize = true;
@@ -82,6 +90,17 @@ public class AddCommand extends GitCommand<DirCache> {
* A directory name (e.g. <code>dir</code> to add <code>dir/file1</code> and
* <code>dir/file2</code>) can also be given to add all files in the
* directory, recursively. Fileglobs (e.g. *.c) are not yet supported.
+ * </p>
+ * <p>
+ * If a pattern {@code "."} is added, all changes in the git repository's
+ * working tree will be added.
+ * </p>
+ * <p>
+ * File patterns are required unless {@code isUpdate() == true} or
+ * {@link #setAll(boolean)} is called. If so and no file patterns are given,
+ * all changes will be added (i.e., a file pattern of {@code "."} is
+ * implied).
+ * </p>
*
* @param filepattern
* repository-relative path of file/directory to add (with
@@ -113,15 +132,41 @@ public class AddCommand extends GitCommand<DirCache> {
* Executes the {@code Add} command. Each instance of this class should only
* be used for one invocation of the command. Don't call this method twice
* on an instance.
+ * </p>
+ *
+ * @throws JGitInternalException
+ * on errors, but also if {@code isUpdate() == true} _and_
+ * {@link #setAll(boolean)} had been called
+ * @throws NoFilepatternException
+ * if no file patterns are given if {@code isUpdate() == false}
+ * and {@link #setAll(boolean)} was not called
*/
@Override
public DirCache call() throws GitAPIException, NoFilepatternException {
-
- if (filepatterns.isEmpty())
- throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired);
checkCallable();
+
+ if (update && all != null) {
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().illegalCombinationOfArguments,
+ "--update", "--all/--no-all")); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ boolean addAll;
+ if (filepatterns.isEmpty()) {
+ if (update || all != null) {
+ addAll = true;
+ } else {
+ throw new NoFilepatternException(
+ JGitText.get().atLeastOnePatternIsRequired);
+ }
+ } else {
+ addAll = filepatterns.contains("."); //$NON-NLS-1$
+ if (all == null && !update) {
+ all = Boolean.TRUE;
+ }
+ }
+ boolean stageDeletions = update || (all != null && all.booleanValue());
+
DirCache dc = null;
- boolean addAll = filepatterns.contains("."); //$NON-NLS-1$
try (ObjectInserter inserter = repo.newObjectInserter();
NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
@@ -181,7 +226,8 @@ public class AddCommand extends GitCommand<DirCache> {
if (f == null) { // working tree file does not exist
if (entry != null
- && (!update || GITLINK == entry.getFileMode())) {
+ && (!stageDeletions
+ || GITLINK == entry.getFileMode())) {
builder.add(entry);
}
continue;
@@ -252,7 +298,8 @@ public class AddCommand extends GitCommand<DirCache> {
}
/**
- * Set whether to only match against already tracked files
+ * Set whether to only match against already tracked files. If
+ * {@code update == true}, re-sets a previous {@link #setAll(boolean)}.
*
* @param update
* If set to true, the command only matches {@code filepattern}
@@ -314,4 +361,32 @@ public class AddCommand extends GitCommand<DirCache> {
public boolean isRenormalize() {
return renormalize;
}
+
+ /**
+ * Defines whether the command will use '--all' mode: update existing index
+ * entries, add new entries, and remove index entries for which there is no
+ * file. (In other words: also stage deletions.)
+ * <p>
+ * The setting is independent of {@link #setUpdate(boolean)}.
+ * </p>
+ *
+ * @param all
+ * whether to enable '--all' mode
+ * @return {@code this}
+ * @since 7.2
+ */
+ public AddCommand setAll(boolean all) {
+ this.all = Boolean.valueOf(all);
+ return this;
+ }
+
+ /**
+ * Tells whether '--all' has been set for this command.
+ *
+ * @return {@code true} if it was set; {@code false} otherwise
+ * @since 7.2
+ */
+ public boolean isAll() {
+ return all != null && all.booleanValue();
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
index c133219d4d..32c242f271 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -644,24 +644,6 @@ public class CheckoutCommand extends GitCommand<Ref> {
/**
* Specify to force the ref update in case of a branch switch.
*
- * @param force
- * if <code>true</code> and the branch with the given name
- * already exists, the start-point of an existing branch will be
- * set to a new start-point; if false, the existing branch will
- * not be changed
- * @return this instance
- * @deprecated this method was badly named comparing its semantics to native
- * git's checkout --force option, use
- * {@link #setForceRefUpdate(boolean)} instead
- */
- @Deprecated
- public CheckoutCommand setForce(boolean force) {
- return setForceRefUpdate(force);
- }
-
- /**
- * Specify to force the ref update in case of a branch switch.
- *
* In releases prior to 5.2 this method was called setForce() but this name
* was misunderstood to implement native git's --force option, which is not
* true.
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 3e034f1a6a..4a536b9534 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -67,6 +67,8 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
private boolean bare;
+ private boolean relativePaths;
+
private FS fs;
private String remote = Constants.DEFAULT_REMOTE_NAME;
@@ -264,6 +266,7 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
private Repository init() throws GitAPIException {
InitCommand command = Git.init();
command.setBare(bare);
+ command.setRelativeDirs(relativePaths);
if (fs != null) {
command.setFs(fs);
}
@@ -555,6 +558,20 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
}
/**
+ * Set whether the cloned repository shall use relative paths for GIT_DIR
+ * and GIT_WORK_TREE
+ *
+ * @param relativePaths
+ * if true, use relative paths for GIT_DIR and GIT_WORK_TREE
+ * @return this instance
+ * @since 7.2
+ */
+ public CloneCommand setRelativePaths(boolean relativePaths) {
+ this.relativePaths = relativePaths;
+ return this;
+ }
+
+ /**
* Set the file system abstraction to be used for repositories created by
* this command.
*
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 a1a2cc09d2..a7d409c3f5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -51,9 +51,6 @@ import org.eclipse.jgit.lib.CommitConfig.CleanupMode;
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;
import org.eclipse.jgit.lib.PersonIdent;
@@ -62,6 +59,8 @@ import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
+import org.eclipse.jgit.lib.Signer;
+import org.eclipse.jgit.lib.Signers;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
@@ -129,7 +128,7 @@ public class CommitCommand extends GitCommand<RevCommit> {
private String signingKey;
- private GpgSigner gpgSigner;
+ private Signer signer;
private GpgConfig gpgConfig;
@@ -319,30 +318,22 @@ public class CommitCommand extends GitCommand<RevCommit> {
}
}
- private void sign(CommitBuilder commit) throws ServiceUnavailableException,
- CanceledException, UnsupportedSigningFormatException {
- if (gpgSigner == null) {
- gpgSigner = GpgSigner.getDefault();
- if (gpgSigner == null) {
- throw new ServiceUnavailableException(
- JGitText.get().signingServiceUnavailable);
+ private void sign(CommitBuilder commit)
+ throws CanceledException, IOException,
+ UnsupportedSigningFormatException {
+ if (signer == null) {
+ signer = Signers.get(gpgConfig.getKeyFormat());
+ if (signer == null) {
+ throw new UnsupportedSigningFormatException(MessageFormat
+ .format(JGitText.get().signatureTypeUnknown,
+ gpgConfig.getKeyFormat().toConfigValue()));
}
}
if (signingKey == null) {
signingKey = gpgConfig.getSigningKey();
}
- 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);
- }
+ signer.signObject(repo, gpgConfig, commit, committer, signingKey,
+ credentialsProvider);
}
private void updateRef(RepositoryState state, ObjectId headId,
@@ -1097,22 +1088,22 @@ public class CommitCommand extends GitCommand<RevCommit> {
}
/**
- * Sets the {@link GpgSigner} to use if the commit is to be signed.
+ * Sets the {@link Signer} 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
+ * @since 7.0
*/
- public CommitCommand setGpgSigner(GpgSigner signer) {
+ public CommitCommand setSigner(Signer signer) {
checkCallable();
- this.gpgSigner = signer;
+ this.signer = signer;
return this;
}
/**
* Sets an external {@link GpgConfig} to use. Whether it will be used is at
- * the discretion of the {@link #setGpgSigner(GpgSigner)}.
+ * the discretion of the {@link #setSigner(Signer)}.
*
* @param config
* to set; if {@code null}, the config will be loaded from the
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
index 805a886392..d2526287f9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
@@ -15,11 +15,11 @@ import static org.eclipse.jgit.lib.TypedConfigGetter.UNSET_INT;
import java.io.IOException;
import java.text.MessageFormat;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -76,6 +76,11 @@ public class DescribeCommand extends GitCommand<String> {
private List<FileNameMatcher> matchers = new ArrayList<>();
/**
+ * Pattern matchers to be applied to tags for exclusion.
+ */
+ private List<FileNameMatcher> excludeMatchers = new ArrayList<>();
+
+ /**
* Whether to use all refs in the refs/ namespace
*/
private boolean useAll;
@@ -263,6 +268,27 @@ public class DescribeCommand extends GitCommand<String> {
return this;
}
+ /**
+ * Sets one or more {@code glob(7)} patterns that tags must not match to be
+ * considered. If multiple patterns are provided, they will all be applied.
+ *
+ * @param patterns
+ * the {@code glob(7)} pattern or patterns
+ * @return {@code this}
+ * @throws org.eclipse.jgit.errors.InvalidPatternException
+ * if the pattern passed in was invalid.
+ * @see <a href=
+ * "https://www.kernel.org/pub/software/scm/git/docs/git-describe.html"
+ * >Git documentation about describe</a>
+ * @since 7.2
+ */
+ public DescribeCommand setExclude(String... patterns) throws InvalidPatternException {
+ for (String p : patterns) {
+ excludeMatchers.add(new FileNameMatcher(p, null));
+ }
+ return this;
+ }
+
private final Comparator<Ref> TAG_TIE_BREAKER = new Comparator<>() {
@Override
@@ -274,25 +300,28 @@ public class DescribeCommand extends GitCommand<String> {
}
}
- private Date tagDate(Ref tag) throws IOException {
+ private Instant tagDate(Ref tag) throws IOException {
RevTag t = w.parseTag(tag.getObjectId());
w.parseBody(t);
- return t.getTaggerIdent().getWhen();
+ return t.getTaggerIdent().getWhenAsInstant();
}
};
private Optional<Ref> getBestMatch(List<Ref> tags) {
if (tags == null || tags.isEmpty()) {
return Optional.empty();
- } else if (matchers.isEmpty()) {
+ } else if (matchers.isEmpty() && excludeMatchers.isEmpty()) {
Collections.sort(tags, TAG_TIE_BREAKER);
return Optional.of(tags.get(0));
- } else {
+ }
+
+ Stream<Ref> matchingTags;
+ if (!matchers.isEmpty()) {
// Find the first tag that matches in the stream of all tags
// filtered by matchers ordered by tie break order
- Stream<Ref> matchingTags = Stream.empty();
+ matchingTags = Stream.empty();
for (FileNameMatcher matcher : matchers) {
- Stream<Ref> m = tags.stream().filter(
+ Stream<Ref> m = tags.stream().filter( //
tag -> {
matcher.append(formatRefName(tag.getName()));
boolean result = matcher.isMatch();
@@ -301,8 +330,22 @@ public class DescribeCommand extends GitCommand<String> {
});
matchingTags = Stream.of(matchingTags, m).flatMap(i -> i);
}
- return matchingTags.sorted(TAG_TIE_BREAKER).findFirst();
+ } else {
+ // If there are no matchers, there are only excluders
+ // Assume all tags match for now before applying excluders
+ matchingTags = tags.stream();
+ }
+
+ for (FileNameMatcher matcher : excludeMatchers) {
+ matchingTags = matchingTags.filter( //
+ tag -> {
+ matcher.append(formatRefName(tag.getName()));
+ boolean result = matcher.isMatch();
+ matcher.reset();
+ return !result;
+ });
}
+ return matchingTags.sorted(TAG_TIE_BREAKER).findFirst();
}
private ObjectId getObjectIdFromRef(Ref r) throws JGitInternalException {
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 0713c38931..f24127bd51 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -124,7 +124,7 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
FetchRecurseSubmodulesMode mode = repo.getConfig().getEnum(
FetchRecurseSubmodulesMode.values(),
ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
- ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES, null);
+ ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES);
if (mode != null) {
return mode;
}
@@ -132,7 +132,7 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
// Fall back to fetch.recurseSubmodules, if set
mode = repo.getConfig().getEnum(FetchRecurseSubmodulesMode.values(),
ConfigConstants.CONFIG_FETCH_SECTION, null,
- ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES, null);
+ ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES);
if (mode != null) {
return mode;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
index 88d7e91860..f6935e1c67 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
@@ -12,6 +12,7 @@ package org.eclipse.jgit.api;
import java.io.IOException;
import java.text.MessageFormat;
import java.text.ParseException;
+import java.time.Instant;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
@@ -59,7 +60,7 @@ public class GarbageCollectCommand extends GitCommand<Properties> {
private ProgressMonitor monitor;
- private Date expire;
+ private Instant expire;
private PackConfig pconfig;
@@ -98,8 +99,29 @@ public class GarbageCollectCommand extends GitCommand<Properties> {
* @param expire
* minimal age of objects to be pruned.
* @return this instance
+ * @deprecated use {@link #setExpire(Instant)} instead
*/
+ @Deprecated(since = "7.2")
public GarbageCollectCommand setExpire(Date expire) {
+ if (expire != null) {
+ this.expire = expire.toInstant();
+ }
+ return this;
+ }
+
+ /**
+ * During gc() or prune() each unreferenced, loose object which has been
+ * created or modified after <code>expire</code> will not be pruned. Only
+ * older objects may be pruned. If set to null then every object is a
+ * candidate for pruning. Use {@link org.eclipse.jgit.util.GitTimeParser} to
+ * parse time formats used by git gc.
+ *
+ * @param expire
+ * minimal age of objects to be pruned.
+ * @return this instance
+ * @since 7.2
+ */
+ public GarbageCollectCommand setExpire(Instant expire) {
this.expire = expire;
return this;
}
@@ -108,8 +130,8 @@ public class GarbageCollectCommand extends GitCommand<Properties> {
* Whether to use aggressive mode or not. If set to true JGit behaves more
* similar to native git's "git gc --aggressive". If set to
* <code>true</code> compressed objects found in old packs are not reused
- * but every object is compressed again. Configuration variables
- * pack.window and pack.depth are set to 250 for this GC.
+ * but every object is compressed again. Configuration variables pack.window
+ * and pack.depth are set to 250 for this GC.
*
* @since 3.6
* @param aggressive
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 3dc53ec248..5bc035a46a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -714,6 +714,16 @@ public class Git implements AutoCloseable {
}
/**
+ * Return a command object to execute a {@code PackRefs} command
+ *
+ * @return a {@link org.eclipse.jgit.api.PackRefsCommand}
+ * @since 7.1
+ */
+ public PackRefsCommand packRefs() {
+ return new PackRefsCommand(repo);
+ }
+
+ /**
* Return a command object to find human-readable names of revisions.
*
* @return a {@link org.eclipse.jgit.api.NameRevCommand}.
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 240290f4f9..1da71aa6eb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
@@ -19,6 +19,7 @@ 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.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
@@ -44,6 +45,8 @@ public class InitCommand implements Callable<Git> {
private String initialBranch;
+ private boolean relativePaths;
+
/**
* {@inheritDoc}
* <p>
@@ -100,7 +103,11 @@ public class InitCommand implements Callable<Git> {
: initialBranch);
Repository repository = builder.build();
if (!repository.getObjectDatabase().exists())
- repository.create(bare);
+ if (repository instanceof FileRepository) {
+ ((FileRepository) repository).create(bare, relativePaths);
+ } else {
+ repository.create(bare);
+ }
return new Git(repository, true);
} catch (IOException | ConfigInvalidException e) {
throw new JGitInternalException(e.getMessage(), e);
@@ -214,4 +221,18 @@ public class InitCommand implements Callable<Git> {
this.initialBranch = branch;
return this;
}
+
+ /**
+ * * Set whether the repository shall use relative paths for GIT_DIR and
+ * GIT_WORK_TREE
+ *
+ * @param relativePaths
+ * if true, use relative paths for GIT_DIR and GIT_WORK_TREE
+ * @return {@code this}
+ * @since 7.2
+ */
+ public InitCommand setRelativeDirs(boolean relativePaths) {
+ this.relativePaths = relativePaths;
+ return this;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PackRefsCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PackRefsCommand.java
new file mode 100644
index 0000000000..29a69c5ac4
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PackRefsCommand.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Optimize storage of references.
+ *
+ * @since 7.1
+ */
+public class PackRefsCommand extends GitCommand<String> {
+ private ProgressMonitor monitor;
+
+ private boolean all;
+
+ /**
+ * Creates a new {@link PackRefsCommand} instance with default values.
+ *
+ * @param repo
+ * the repository this command will be used on
+ */
+ public PackRefsCommand(Repository repo) {
+ super(repo);
+ this.monitor = NullProgressMonitor.INSTANCE;
+ }
+
+ /**
+ * Set progress monitor
+ *
+ * @param monitor
+ * a progress monitor
+ * @return this instance
+ */
+ public PackRefsCommand setProgressMonitor(ProgressMonitor monitor) {
+ this.monitor = monitor;
+ return this;
+ }
+
+ /**
+ * Specify whether to pack all the references.
+ *
+ * @param all
+ * if <code>true</code> all the loose refs will be packed
+ * @return this instance
+ */
+ public PackRefsCommand setAll(boolean all) {
+ this.all = all;
+ return this;
+ }
+
+ /**
+ * Whether to pack all the references
+ *
+ * @return whether to pack all the references
+ */
+ public boolean isAll() {
+ return all;
+ }
+
+ @Override
+ public String call() throws GitAPIException {
+ checkCallable();
+ try {
+ repo.getRefDatabase().packRefs(monitor, this);
+ return JGitText.get().packRefsSuccessful;
+ } catch (IOException e) {
+ throw new JGitInternalException(JGitText.get().packRefsFailed, e);
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index 83ae0fc9d4..4b2cee45c2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -533,9 +533,9 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
Config config) {
BranchRebaseMode mode = config.getEnum(BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION,
- branchName, ConfigConstants.CONFIG_KEY_REBASE, null);
+ branchName, ConfigConstants.CONFIG_KEY_REBASE);
if (mode == null) {
- mode = config.getEnum(BranchRebaseMode.values(),
+ mode = config.getEnum(
ConfigConstants.CONFIG_PULL_SECTION, null,
ConfigConstants.CONFIG_KEY_REBASE, BranchRebaseMode.NONE);
}
@@ -549,7 +549,7 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
Config config = repo.getConfig();
Merge ffMode = config.getEnum(Merge.values(),
ConfigConstants.CONFIG_PULL_SECTION, null,
- ConfigConstants.CONFIG_KEY_FF, null);
+ ConfigConstants.CONFIG_KEY_FF);
return ffMode != null ? FastForwardMode.valueOf(ffMode) : null;
}
}
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 858bd961cd..3ae7a6c81e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -18,6 +18,8 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -1835,23 +1837,26 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
// the time is saved as <seconds since 1970> <timezone offset>
int timeStart = 0;
- if (time.startsWith("@")) //$NON-NLS-1$
+ if (time.startsWith("@")) { //$NON-NLS-1$
timeStart = 1;
- else
+ } else {
timeStart = 0;
- long when = Long
- .parseLong(time.substring(timeStart, time.indexOf(' '))) * 1000;
+ }
+ Instant when = Instant.ofEpochSecond(
+ Long.parseLong(time.substring(timeStart, time.indexOf(' '))));
String tzOffsetString = time.substring(time.indexOf(' ') + 1);
int multiplier = -1;
- if (tzOffsetString.charAt(0) == '+')
+ if (tzOffsetString.charAt(0) == '+') {
multiplier = 1;
+ }
int hours = Integer.parseInt(tzOffsetString.substring(1, 3));
int minutes = Integer.parseInt(tzOffsetString.substring(3, 5));
// this is in format (+/-)HHMM (hours and minutes)
- // we need to convert into minutes
- int tz = (hours * 60 + minutes) * multiplier;
- if (name != null && email != null)
+ ZoneOffset tz = ZoneOffset.ofHoursMinutes(hours * multiplier,
+ minutes * multiplier);
+ if (name != null && email != null) {
return new PersonIdent(name, email, when, tz);
+ }
return null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
index dead2749b7..a149649004 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
@@ -68,7 +68,7 @@ public class ReflogCommand extends GitCommand<Collection<ReflogEntry>> {
checkCallable();
try {
- ReflogReader reader = repo.getReflogReader(ref);
+ ReflogReader reader = repo.getRefDatabase().getReflogReader(ref);
if (reader == null)
throw new RefNotFoundException(MessageFormat.format(
JGitText.get().refNotResolved, ref));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
index 553fc2e7a0..ad553f07a9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
@@ -49,18 +49,6 @@ public class RemoteRemoveCommand extends GitCommand<RemoteConfig> {
/**
* The name of the remote to remove.
*
- * @param name
- * a remote name
- * @deprecated use {@link #setRemoteName} instead
- */
- @Deprecated
- public void setName(String name) {
- this.remoteName = name;
- }
-
- /**
- * The name of the remote to remove.
- *
* @param remoteName
* a remote name
* @return {@code this}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
index e3d01861fc..68ddce361f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
@@ -71,18 +71,6 @@ public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
/**
* The name of the remote to change the URL for.
*
- * @param name
- * a remote name
- * @deprecated use {@link #setRemoteName} instead
- */
- @Deprecated
- public void setName(String name) {
- this.remoteName = name;
- }
-
- /**
- * The name of the remote to change the URL for.
- *
* @param remoteName
* a remote remoteName
* @return {@code this}
@@ -96,18 +84,6 @@ public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
/**
* The new URL for the remote.
*
- * @param uri
- * an URL for the remote
- * @deprecated use {@link #setRemoteUri} instead
- */
- @Deprecated
- public void setUri(URIish uri) {
- this.remoteUri = uri;
- }
-
- /**
- * The new URL for the remote.
- *
* @param remoteUri
* an URL for the remote
* @return {@code this}
@@ -121,23 +97,6 @@ public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
/**
* Whether to change the push URL of the remote instead of the fetch URL.
*
- * @param push
- * <code>true</code> to set the push url, <code>false</code> to
- * set the fetch url
- * @deprecated use {@link #setUriType} instead
- */
- @Deprecated
- public void setPush(boolean push) {
- if (push) {
- setUriType(UriType.PUSH);
- } else {
- setUriType(UriType.FETCH);
- }
- }
-
- /**
- * Whether to change the push URL of the remote instead of the fetch URL.
- *
* @param type
* the <code>UriType</code> value to set
* @return {@code this}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
index 855c3b1cf3..6643c83662 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> and others
+ * Copyright (C) 2010, 2024 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
@@ -143,8 +143,8 @@ public class RevertCommand extends GitCommand<RevCommit> {
merger.setCommitNames(new String[] {
"BASE", ourName, revertName }); //$NON-NLS-1$
- String shortMessage = "Revert \"" + srcCommit.getShortMessage() //$NON-NLS-1$
- + "\""; //$NON-NLS-1$
+ String shortMessage = "Revert \"" //$NON-NLS-1$
+ + srcCommit.getFirstMessageLine() + '"';
String newMessage = shortMessage + "\n\n" //$NON-NLS-1$
+ "This reverts commit " + srcCommit.getId().getName() //$NON-NLS-1$
+ ".\n"; //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
index e4157286f1..b0b715e06b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -263,18 +263,6 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
/**
* Whether to restore the index state
*
- * @param applyIndex
- * true (default) if the command should restore the index state
- * @deprecated use {@link #setRestoreIndex} instead
- */
- @Deprecated
- public void setApplyIndex(boolean applyIndex) {
- this.restoreIndex = applyIndex;
- }
-
- /**
- * Whether to restore the index state
- *
* @param restoreIndex
* true (default) if the command should restore the index state
* @return {@code this}
@@ -319,19 +307,6 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
/**
* Whether the command should restore untracked files
*
- * @param applyUntracked
- * true (default) if the command should restore untracked files
- * @since 3.4
- * @deprecated use {@link #setRestoreUntracked} instead
- */
- @Deprecated
- public void setApplyUntracked(boolean applyUntracked) {
- this.restoreUntracked = applyUntracked;
- }
-
- /**
- * Whether the command should restore untracked files
- *
* @param restoreUntracked
* true (default) if the command should restore untracked files
* @return {@code this}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
index 23fbe0197f..2dba0ef0f2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
@@ -165,7 +165,8 @@ public class StashDropCommand extends GitCommand<ObjectId> {
List<ReflogEntry> entries;
try {
- ReflogReader reader = repo.getReflogReader(R_STASH);
+ ReflogReader reader = repo.getRefDatabase()
+ .getReflogReader(R_STASH);
if (reader == null) {
throw new RefNotFoundException(MessageFormat
.format(JGitText.get().refNotResolved, stashRef));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
index 8fb5d60b85..5105dfc2e5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
@@ -176,8 +176,9 @@ public class SubmoduleAddCommand extends
CloneCommand clone = Git.cloneRepository();
configure(clone);
clone.setDirectory(moduleDirectory);
- clone.setGitDir(new File(new File(repo.getDirectory(),
- Constants.MODULES), path));
+ clone.setGitDir(new File(
+ new File(repo.getCommonDirectory(), Constants.MODULES), path));
+ clone.setRelativePaths(true);
clone.setURI(resolvedUri);
if (monitor != null)
clone.setProgressMonitor(monitor);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
index df73164161..5e4b2ee0b7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
@@ -28,6 +28,7 @@ import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.internal.storage.file.LockFile;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -39,6 +40,7 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+import org.eclipse.jgit.util.FileUtils;
/**
* A class used to execute a submodule update command.
@@ -62,6 +64,8 @@ public class SubmoduleUpdateCommand extends
private boolean fetch = false;
+ private boolean clonedRestored;
+
/**
* <p>
* Constructor for SubmoduleUpdateCommand.
@@ -116,25 +120,77 @@ public class SubmoduleUpdateCommand extends
return this;
}
+ private static boolean submoduleExists(File gitDir) {
+ if (gitDir != null && gitDir.isDirectory()) {
+ File[] files = gitDir.listFiles();
+ return files != null && files.length != 0;
+ }
+ return false;
+ }
+
+ private static void restoreSubmodule(File gitDir, File workingTree)
+ throws IOException {
+ LockFile dotGitLock = new LockFile(
+ new File(workingTree, Constants.DOT_GIT));
+ if (dotGitLock.lock()) {
+ String content = Constants.GITDIR
+ + getRelativePath(gitDir, workingTree);
+ dotGitLock.write(Constants.encode(content));
+ dotGitLock.commit();
+ }
+ }
+
+ private static String getRelativePath(File gitDir, File workingTree) {
+ File relPath;
+ try {
+ relPath = workingTree.toPath().relativize(gitDir.toPath())
+ .toFile();
+ } catch (IllegalArgumentException e) {
+ relPath = gitDir;
+ }
+ return FileUtils.pathToString(relPath);
+ }
+
+ private String determineUpdateMode(String mode) {
+ if (clonedRestored) {
+ return ConfigConstants.CONFIG_KEY_CHECKOUT;
+ }
+ return mode;
+ }
+
private Repository getOrCloneSubmodule(SubmoduleWalk generator, String url)
throws IOException, GitAPIException {
Repository repository = generator.getRepository();
+ boolean restored = false;
+ boolean cloned = false;
if (repository == null) {
- if (callback != null) {
- callback.cloningSubmodule(generator.getPath());
- }
- CloneCommand clone = Git.cloneRepository();
- configure(clone);
- clone.setURI(url);
- clone.setDirectory(generator.getDirectory());
- clone.setGitDir(
- new File(new File(repo.getDirectory(), Constants.MODULES),
- generator.getPath()));
- if (monitor != null) {
- clone.setProgressMonitor(monitor);
+ File gitDir = new File(
+ new File(repo.getCommonDirectory(), Constants.MODULES),
+ generator.getPath());
+ if (submoduleExists(gitDir)) {
+ restoreSubmodule(gitDir, generator.getDirectory());
+ restored = true;
+ clonedRestored = true;
+ repository = generator.getRepository();
+ } else {
+ if (callback != null) {
+ callback.cloningSubmodule(generator.getPath());
+ }
+ CloneCommand clone = Git.cloneRepository();
+ configure(clone);
+ clone.setURI(url);
+ clone.setDirectory(generator.getDirectory());
+ clone.setGitDir(gitDir);
+ clone.setRelativePaths(true);
+ if (monitor != null) {
+ clone.setProgressMonitor(monitor);
+ }
+ repository = clone.call().getRepository();
+ cloned = true;
+ clonedRestored = true;
}
- repository = clone.call().getRepository();
- } else if (this.fetch) {
+ }
+ if ((this.fetch || restored) && !cloned) {
if (fetchCallback != null) {
fetchCallback.fetchingSubmodule(generator.getPath());
}
@@ -171,15 +227,17 @@ public class SubmoduleUpdateCommand extends
continue;
// Skip submodules not registered in parent repository's config
String url = generator.getConfigUrl();
- if (url == null)
+ if (url == null) {
continue;
-
+ }
+ clonedRestored = false;
try (Repository submoduleRepo = getOrCloneSubmodule(generator,
url); RevWalk walk = new RevWalk(submoduleRepo)) {
RevCommit commit = walk
.parseCommit(generator.getObjectId());
- String update = generator.getConfigUpdate();
+ String update = determineUpdateMode(
+ generator.getConfigUpdate());
if (ConfigConstants.CONFIG_KEY_MERGE.equals(update)) {
MergeCommand merge = new MergeCommand(submoduleRepo);
merge.include(commit);
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 3edaf5e748..cc8589fa1c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
@@ -18,14 +18,11 @@ 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;
@@ -33,6 +30,8 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Signer;
+import org.eclipse.jgit.lib.Signers;
import org.eclipse.jgit.lib.TagBuilder;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -79,7 +78,7 @@ public class TagCommand extends GitCommand<Ref> {
private GpgConfig gpgConfig;
- private GpgObjectSigner gpgSigner;
+ private Signer signer;
private CredentialsProvider credentialsProvider;
@@ -133,9 +132,9 @@ public class TagCommand extends GitCommand<Ref> {
newTag.setTagger(tagger);
newTag.setObjectId(id);
- if (gpgSigner != null) {
- gpgSigner.signObject(newTag, signingKey, tagger,
- credentialsProvider, gpgConfig);
+ if (signer != null) {
+ signer.signObject(repo, gpgConfig, newTag, tagger, signingKey,
+ credentialsProvider);
}
// write the tag object
@@ -196,15 +195,12 @@ public class TagCommand extends GitCommand<Ref> {
*
* @throws InvalidTagNameException
* if the tag name is null or invalid
- * @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()
- throws InvalidTagNameException, ServiceUnavailableException,
- UnsupportedSigningFormatException {
+ throws InvalidTagNameException, UnsupportedSigningFormatException {
if (name == null
|| !Repository.isValidRefName(Constants.R_TAGS + name)) {
throw new InvalidTagNameException(
@@ -230,16 +226,15 @@ public class TagCommand extends GitCommand<Ref> {
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);
+ if (signer == null) {
+ signer = Signers.get(gpgConfig.getKeyFormat());
+ if (signer == null) {
+ throw new UnsupportedSigningFormatException(
+ MessageFormat.format(
+ JGitText.get().signatureTypeUnknown,
+ gpgConfig.getKeyFormat()
+ .toConfigValue()));
}
- gpgSigner = (GpgObjectSigner) signer;
}
// The message of a signed tag must end in a newline because
// the signature will be appended.
@@ -326,22 +321,22 @@ public class TagCommand extends GitCommand<Ref> {
}
/**
- * Sets the {@link GpgSigner} to use if the commit is to be signed.
+ * Sets the {@link Signer} 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
+ * @since 7.0
*/
- public TagCommand setGpgSigner(GpgObjectSigner signer) {
+ public TagCommand setSigner(Signer signer) {
checkCallable();
- this.gpgSigner = signer;
+ this.signer = signer;
return this;
}
/**
* Sets an external {@link GpgConfig} to use. Whether it will be used is at
- * the discretion of the {@link #setGpgSigner(GpgObjectSigner)}.
+ * the discretion of the {@link #setSigner(Signer)}.
*
* @param config
* to set; if {@code null}, the config will be loaded from the
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java
index 21cddf75b7..f5f4b06e45 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java
@@ -9,7 +9,7 @@
*/
package org.eclipse.jgit.api;
-import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.lib.SignatureVerifier;
import org.eclipse.jgit.revwalk.RevObject;
/**
@@ -34,8 +34,9 @@ public interface VerificationResult {
* Retrieves the signature verification result.
*
* @return the result, or {@code null} if none was computed
+ * @since 7.0
*/
- GpgSignatureVerifier.SignatureVerification getVerification();
+ SignatureVerifier.SignatureVerification getVerification();
/**
* Retrieves the git object of which the signature was verified.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java
index 6a2a44ea2d..487ff04323 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java
@@ -25,11 +25,10 @@ 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.lib.SignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.SignatureVerifiers;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -65,12 +64,8 @@ public class VerifySignatureCommand extends GitCommand<Map<String, VerificationR
private VerifyMode mode = VerifyMode.ANY;
- private GpgSignatureVerifier verifier;
-
private GpgConfig config;
- private boolean ownVerifier;
-
/**
* Creates a new {@link VerifySignatureCommand} for the given {@link Repository}.
*
@@ -140,22 +135,7 @@ public class VerifySignatureCommand extends GitCommand<Map<String, VerificationR
}
/**
- * 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)}.
+ * Sets an external {@link GpgConfig} to use.
*
* @param config
* to set; if {@code null}, the config will be loaded from the
@@ -170,16 +150,6 @@ public class VerifySignatureCommand extends GitCommand<Map<String, VerificationR
}
/**
- * 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.
@@ -193,9 +163,6 @@ public class VerifySignatureCommand extends GitCommand<Map<String, VerificationR
*
* @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
@@ -207,16 +174,6 @@ public class VerifySignatureCommand extends GitCommand<Map<String, VerificationR
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());
}
@@ -239,10 +196,6 @@ public class VerifySignatureCommand extends GitCommand<Map<String, VerificationR
} catch (IOException e) {
throw new JGitInternalException(
JGitText.get().signatureVerificationError, e);
- } finally {
- if (ownVerifier) {
- verifier.clear();
- }
}
return result;
}
@@ -258,8 +211,8 @@ public class VerifySignatureCommand extends GitCommand<Map<String, VerificationR
}
if (type == Constants.OBJ_COMMIT || type == Constants.OBJ_TAG) {
try {
- GpgSignatureVerifier.SignatureVerification verification = verifier
- .verifySignature(object, config);
+ SignatureVerification verification = SignatureVerifiers
+ .verify(repo, config, object);
if (verification == null) {
// Not signed
return null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
index fe3e22a21f..9c4d8700a2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
@@ -9,6 +9,8 @@
*/
package org.eclipse.jgit.attributes;
+import org.eclipse.jgit.annotations.Nullable;
+
/**
* Represents an attribute.
* <p>
@@ -139,6 +141,7 @@ public final class Attribute {
*
* @return the attribute value (may be <code>null</code>)
*/
+ @Nullable
public String getValue() {
return value;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
index 77967df2e5..2d499cafce 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
@@ -28,6 +28,8 @@ import org.eclipse.jgit.blame.Candidate.BlobCandidate;
import org.eclipse.jgit.blame.Candidate.HeadCandidate;
import org.eclipse.jgit.blame.Candidate.ReverseCandidate;
import org.eclipse.jgit.blame.ReverseWalk.ReverseCommit;
+import org.eclipse.jgit.blame.cache.BlameCache;
+import org.eclipse.jgit.blame.cache.CacheRegion;
import org.eclipse.jgit.diff.DiffAlgorithm;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
@@ -129,8 +131,19 @@ public class BlameGenerator implements AutoCloseable {
/** Blame is currently assigned to this source. */
private Candidate outCandidate;
+
private Region outRegion;
+ private final BlameCache blameCache;
+
+ /**
+ * Blame in reverse order needs the source lines, but we don't have them in
+ * the cache. We need to ignore the cache in that case.
+ */
+ private boolean useCache = true;
+
+ private final Stats stats = new Stats();
+
/**
* Create a blame generator for the repository and path (relative to
* repository)
@@ -142,6 +155,25 @@ public class BlameGenerator implements AutoCloseable {
* repository).
*/
public BlameGenerator(Repository repository, String path) {
+ this(repository, path, null);
+ }
+
+ /**
+ * Create a blame generator for the repository and path (relative to
+ * repository)
+ *
+ * @param repository
+ * repository to access revision data from.
+ * @param path
+ * initial path of the file to start scanning (relative to the
+ * repository).
+ * @param blameCache
+ * previously calculated blames. This generator will *not*
+ * populate it, just consume it.
+ * @since 7.2
+ */
+ public BlameGenerator(Repository repository, String path,
+ @Nullable BlameCache blameCache) {
this.repository = repository;
this.resultPath = PathFilter.create(path);
@@ -150,6 +182,7 @@ public class BlameGenerator implements AutoCloseable {
initRevPool(false);
remaining = -1;
+ this.blameCache = blameCache;
}
private void initRevPool(boolean reverse) {
@@ -159,10 +192,12 @@ public class BlameGenerator implements AutoCloseable {
if (revPool != null)
revPool.close();
- if (reverse)
+ if (reverse) {
+ useCache = false;
revPool = new ReverseWalk(getRepository());
- else
+ } else {
revPool = new RevWalk(getRepository());
+ }
SEEN = revPool.newFlag("SEEN"); //$NON-NLS-1$
reader = revPool.getObjectReader();
@@ -245,6 +280,31 @@ public class BlameGenerator implements AutoCloseable {
}
/**
+ * Stats about this generator
+ *
+ * @return the stats of this generator
+ * @since 7.2
+ */
+ public Stats getStats() {
+ return stats;
+ }
+
+ /**
+ * Enable/disable the use of cache (if present). Enabled by default.
+ * <p>
+ * If caller need source line numbers, the generator cannot use the cache
+ * (source lines are not there). Use this method to disable the cache in
+ * that case.
+ *
+ * @param useCache
+ * should this generator use the cache.
+ * @since 7.2
+ */
+ public void setUseCache(boolean useCache) {
+ this.useCache = useCache;
+ }
+
+ /**
* Push a candidate blob onto the generator's traversal stack.
* <p>
* Candidates should be pushed in history order from oldest-to-newest.
@@ -591,6 +651,20 @@ public class BlameGenerator implements AutoCloseable {
Candidate n = pop();
if (n == null)
return done();
+ stats.candidatesVisited += 1;
+ if (blameCache != null && useCache) {
+ List<CacheRegion> cachedBlame = blameCache.get(repository,
+ n.sourceCommit, n.sourcePath.getPath());
+ if (cachedBlame != null) {
+ BlameRegionMerger rb = new BlameRegionMerger(repository,
+ revPool, cachedBlame);
+ Candidate fullyBlamed = rb.mergeCandidate(n);
+ if (fullyBlamed != null) {
+ stats.cacheHit = true;
+ return result(fullyBlamed);
+ }
+ }
+ }
int pCnt = n.getParentCount();
if (pCnt == 1) {
@@ -605,7 +679,7 @@ public class BlameGenerator implements AutoCloseable {
// Do not generate a tip of a reverse. The region
// survives and should not appear to be deleted.
- } else /* if (pCnt == 0) */{
+ } else /* if (pCnt == 0) */ {
// Root commit, with at least one surviving region.
// Assign the remaining blame here.
return result(n);
@@ -846,8 +920,8 @@ public class BlameGenerator implements AutoCloseable {
editList = new EditList(0);
} else {
p.loadText(reader);
- editList = diffAlgorithm.diff(textComparator,
- p.sourceText, n.sourceText);
+ editList = diffAlgorithm.diff(textComparator, p.sourceText,
+ n.sourceText);
}
if (editList.isEmpty()) {
@@ -981,6 +1055,10 @@ public class BlameGenerator implements AutoCloseable {
/**
* Get first line of the source data that has been blamed for the current
* region
+ * <p>
+ * This value is not reliable when the generator is reusing cached values.
+ * Cache doesn't keep the source lines, the returned value is based on the
+ * result and can be off if the region moved in previous commits.
*
* @return first line of the source data that has been blamed for the
* current region. This is line number of where the region was added
@@ -994,6 +1072,10 @@ public class BlameGenerator implements AutoCloseable {
/**
* Get one past the range of the source data that has been blamed for the
* current region
+ * <p>
+ * This value is not reliable when the generator is reusing cached values.
+ * Cache doesn't keep the source lines, the returned value is based on the
+ * result and can be off if the region moved in previous commits.
*
* @return one past the range of the source data that has been blamed for
* the current region. This is line number of where the region was
@@ -1124,4 +1206,39 @@ public class BlameGenerator implements AutoCloseable {
return ent.getChangeType() == ChangeType.RENAME
|| ent.getChangeType() == ChangeType.COPY;
}
+
+ /**
+ * Stats about the work done by the generator
+ *
+ * @since 7.2
+ */
+ public static class Stats {
+
+ /** Candidates taken from the queue */
+ private int candidatesVisited;
+
+ private boolean cacheHit;
+
+ /**
+ * Number of candidates taken from the queue
+ * <p>
+ * The generator could signal it's done without exhausting all
+ * candidates if there is no more remaining lines or the last visited
+ * candidate is found in the cache.
+ *
+ * @return number of candidates taken from the queue
+ */
+ public int getCandidatesVisited() {
+ return candidatesVisited;
+ }
+
+ /**
+ * The generator found a blamed version in the cache
+ *
+ * @return true if we used results from the cache
+ */
+ public boolean isCacheHit() {
+ return cacheHit;
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameRegionMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameRegionMerger.java
new file mode 100644
index 0000000000..67bc6fb789
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameRegionMerger.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2025, Google LLC.
+ *
+ * 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.blame;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jgit.blame.cache.CacheRegion;
+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.eclipse.jgit.treewalk.filter.PathFilter;
+
+/**
+ * Translates an unblamed region into one or more blamed regions, using the
+ * fully blamed data from cache.
+ * <p>
+ * Blamed and unblamed regions are not symmetrical: An unblamed region is just a
+ * range of lines over the file. A blamed region is a Candidate (with the commit
+ * info) with a region inside (the range blamed).
+ */
+class BlameRegionMerger {
+ private final Repository repo;
+
+ private final List<CacheRegion> cachedRegions;
+
+ private final RevWalk rw;
+
+ BlameRegionMerger(Repository repo, RevWalk rw,
+ List<CacheRegion> cachedRegions) {
+ this.repo = repo;
+ List<CacheRegion> sorted = new ArrayList<>(cachedRegions);
+ Collections.sort(sorted);
+ this.cachedRegions = sorted;
+ this.rw = rw;
+ }
+
+ /**
+ * Return one or more candidates blaming all the regions of the "unblamed"
+ * incoming candidate.
+ *
+ * @param candidate
+ * a candidate with a list of unblamed regions
+ * @return A linked list of Candidates with their blamed regions, null if
+ * there was any error.
+ */
+ Candidate mergeCandidate(Candidate candidate) {
+ List<Candidate> newCandidates = new ArrayList<>();
+ Region r = candidate.regionList;
+ while (r != null) {
+ try {
+ newCandidates.addAll(mergeOneRegion(r));
+ } catch (IOException e) {
+ return null;
+ }
+ r = r.next;
+ }
+ return asLinkedCandidate(newCandidates);
+ }
+
+ // Visible for testing
+ List<Candidate> mergeOneRegion(Region region) throws IOException {
+ List<CacheRegion> overlaps = findOverlaps(region);
+ if (overlaps.isEmpty()) {
+ throw new IOException(
+ "Cached blame should cover all lines");
+ }
+ /*
+ * Cached regions cover the whole file. We find first which ones overlap
+ * with our unblamed region. Then we take the overlapping portions with
+ * the corresponding blame.
+ */
+ List<Candidate> candidates = new ArrayList<>();
+ for (CacheRegion overlap : overlaps) {
+ Region blamedRegions = intersectRegions(region, overlap);
+ Candidate c = new Candidate(repo, parse(overlap.getSourceCommit()),
+ PathFilter.create(overlap.getSourcePath()));
+ c.regionList = blamedRegions;
+ candidates.add(c);
+ }
+ return candidates;
+ }
+
+ // Visible for testing
+ List<CacheRegion> findOverlaps(Region unblamed) {
+ int unblamedStart = unblamed.sourceStart;
+ int unblamedEnd = unblamedStart + unblamed.length;
+ List<CacheRegion> overlapping = new ArrayList<>();
+ for (CacheRegion blamed : cachedRegions) {
+ // End is not included
+ if (blamed.getEnd() <= unblamedStart) {
+ // Blamed region is completely before
+ continue;
+ }
+
+ if (blamed.getStart() >= unblamedEnd) {
+ // Blamed region is completely after
+ // Blamed regions are sorted by start position, nothing will
+ // match anymore
+ break;
+ }
+ overlapping.add(blamed);
+ }
+ return overlapping;
+ }
+
+ // Visible for testing
+ /**
+ * Calculate the intersection between a Region and a CacheRegion, adjusting
+ * the start if needed.
+ * <p>
+ * This should be called only if there is an overlap (filtering the cached
+ * regions with {@link #findOverlaps(Region)}), otherwise the result is
+ * meaningless.
+ *
+ * @param unblamed
+ * a region from the blame generator
+ * @param cached
+ * a cached region
+ * @return a new region with the intersection.
+ */
+ static Region intersectRegions(Region unblamed, CacheRegion cached) {
+ int blamedStart = Math.max(cached.getStart(), unblamed.sourceStart);
+ int blamedEnd = Math.min(cached.getEnd(),
+ unblamed.sourceStart + unblamed.length);
+ int length = blamedEnd - blamedStart;
+
+ // result start and source start should move together
+ int blameStartDelta = blamedStart - unblamed.sourceStart;
+ return new Region(unblamed.resultStart + blameStartDelta, blamedStart,
+ length);
+ }
+
+ // Tests can override this, so they don't need a real repo, commit and walk
+ protected RevCommit parse(ObjectId oid) throws IOException {
+ return rw.parseCommit(oid);
+ }
+
+ private static Candidate asLinkedCandidate(List<Candidate> c) {
+ Candidate head = c.get(0);
+ Candidate tail = head;
+ for (int i = 1; i < c.size(); i++) {
+ tail.queueNext = c.get(i);
+ tail = tail.queueNext;
+ }
+ return head;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java
index 5e2746cc7c..48f6b7e250 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java
@@ -79,6 +79,7 @@ public class BlameResult {
BlameResult(BlameGenerator bg, String path, RawText text) {
generator = bg;
+ generator.setUseCache(false);
resultPath = path;
resultContents = text;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/BlameCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/BlameCache.java
new file mode 100644
index 0000000000..d44fb5f62b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/BlameCache.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025, Google LLC.
+ *
+ * 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.blame.cache;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Keeps the blame information for a path at certain commit.
+ * <p>
+ * If there is a result, it covers the whole file at that revision
+ *
+ * @since 7.2
+ */
+public interface BlameCache {
+ /**
+ * Gets the blame of a path at a given commit if available.
+ * <p>
+ * Since this cache is used in blame calculation, this get() method should
+ * only retrieve the cache value, and not re-trigger blame calculation. In
+ * other words, this acts as "getIfPresent", and not "computeIfAbsent".
+ *
+ * @param repo
+ * repository containing the commit
+ * @param commitId
+ * we are looking at the file in this revision
+ * @param path
+ * path a file in the repo
+ *
+ * @return the blame of a path at a given commit or null if not in cache
+ * @throws IOException
+ * error retrieving/parsing values from storage
+ */
+ List<CacheRegion> get(Repository repo, ObjectId commitId, String path)
+ throws IOException;
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/CacheRegion.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/CacheRegion.java
new file mode 100644
index 0000000000..cf3f978044
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/CacheRegion.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2025, Google LLC.
+ *
+ * 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.blame.cache;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Region of the blame of a file.
+ * <p>
+ * Usually all parameters are non-null, except when the Region was created
+ * to fill an unblamed gap (to cover for bugs in the calculation). In that
+ * case, path, commit and author will be null.
+ *
+ * @since 7.2
+ **/
+public class CacheRegion implements Comparable<CacheRegion> {
+ private final String sourcePath;
+
+ private final ObjectId sourceCommit;
+
+ private final int end;
+
+ private final int start;
+
+ /**
+ * A blamed portion of a file
+ *
+ * @param path
+ * location of the file
+ * @param commit
+ * commit that is modifying this region
+ * @param start
+ * first line of this region (inclusive)
+ * @param end
+ * last line of this region (non-inclusive!)
+ */
+ public CacheRegion(String path, ObjectId commit,
+ int start, int end) {
+ allOrNoneNull(path, commit);
+ this.sourcePath = path;
+ this.sourceCommit = commit;
+ this.start = start;
+ this.end = end;
+ }
+
+ /**
+ * First line of this region. Starting by 0, inclusive
+ *
+ * @return first line of this region.
+ */
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * One after last line in this region (or: last line non-inclusive)
+ *
+ * @return one after last line in this region.
+ */
+ public int getEnd() {
+ return end;
+ }
+
+
+ /**
+ * Path of the file this region belongs to
+ *
+ * @return path in the repo/commit
+ */
+ public String getSourcePath() {
+ return sourcePath;
+ }
+
+ /**
+ * Commit this region belongs to
+ *
+ * @return commit for this region
+ */
+ public ObjectId getSourceCommit() {
+ return sourceCommit;
+ }
+
+ @Override
+ public int compareTo(CacheRegion o) {
+ return start - o.start;
+ }
+
+ @SuppressWarnings("nls")
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (sourceCommit != null) {
+ sb.append(sourceCommit.name(), 0, 7).append(' ')
+ .append(" (")
+ .append(sourcePath).append(')');
+ } else {
+ sb.append("<unblamed region>");
+ }
+ sb.append(' ').append("start=").append(start).append(", count=")
+ .append(end - start);
+ return sb.toString();
+ }
+
+ private static void allOrNoneNull(String path, ObjectId commit) {
+ if (path != null && commit != null) {
+ return;
+ }
+
+ if (path == null && commit == null) {
+ return;
+ }
+ throw new IllegalArgumentException(MessageFormat
+ .format(JGitText.get().cacheRegionAllOrNoneNull, path, commit));
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffDriver.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffDriver.java
new file mode 100644
index 0000000000..b74444400e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffDriver.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.diff;
+
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * Built-in drivers for various languages, sorted by name. These drivers will be
+ * used to determine function names for a hunk.
+ * <p>
+ * When writing or updating patterns, assume the contents are syntactically
+ * correct. Patterns can be simple and need not cover all syntactical corner
+ * cases, as long as they are sufficiently permissive.
+ *
+ * @since 6.10.1
+ */
+@SuppressWarnings({"ImmutableEnumChecker", "nls"})
+public enum DiffDriver {
+ /**
+ * Built-in diff driver for <a href=
+ * "https://learn.microsoft.com/en-us/cpp/cpp/cpp-language-reference">c++</a>
+ */
+ cpp(List.of(
+ /* Jump targets or access declarations */
+ "^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:\\s*($|/[/*])"), List.of(
+ /* functions/methods, variables, and compounds at top level */
+ "^((::\\s*)?[A-Za-z_].*)$")),
+ /**
+ * Built-in diff driver for <a href=
+ * "https://devicetree-specification.readthedocs.io/en/stable/source-language.html">device
+ * tree files</a>
+ */
+ dts(List.of(";", "="), List.of(
+ /* lines beginning with a word optionally preceded by '&' or the root */
+ "^[ \\t]*((/[ \\t]*\\{|&?[a-zA-Z_]).*)")),
+ /**
+ * Built-in diff driver for <a href=
+ * "https://docs.oracle.com/javase/specs/jls/se21/html/index.html">java</a>
+ */
+ java(List.of(
+ "^[ \\t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)"),
+ List.of(
+ /* Class, enum, interface, and record declarations */
+ "^[ \\t]*(([a-z-]+[ \\t]+)*(class|enum|interface|record)[ \\t]+.*)$",
+ /* Method definitions; note that constructor signatures are not */
+ /* matched because they are indistinguishable from method calls. */
+ "^[ \\t]*(([A-Za-z_<>&\\]\\[][?&<>.,A-Za-z_0-9]*[ \\t]+)+[A-Za-z_]"
+ + "[A-Za-z_0-9]*[ \\t]*\\([^;]*)$")),
+ /**
+ * Built-in diff driver for
+ * <a href="https://docs.python.org/3/reference/index.html">python</a>
+ */
+ python(List.of("^[ \\t]*((class|(async[ \\t]+)?def)[ \\t].*)$")),
+ /**
+ * Built-in diff driver for
+ * <a href="https://doc.rust-lang.org/reference/introduction.html">rust</a>
+ */
+ rust(List.of("^[\\t ]*((pub(\\([^\\)]+\\))?[\\t ]+)?"
+ + "((async|const|unsafe|extern([\\t ]+\"[^\"]+\"))[\\t ]+)?"
+ + "(struct|enum|union|mod|trait|fn|impl|macro_rules!)[< \\t]+[^;]*)$"));
+
+ private final List<Pattern> negatePatterns;
+
+ private final List<Pattern> matchPatterns;
+
+ DiffDriver(List<String> negate, List<String> match, int flags) {
+ if (negate != null) {
+ this.negatePatterns = negate.stream()
+ .map(r -> Pattern.compile(r, flags))
+ .collect(Collectors.toList());
+ } else {
+ this.negatePatterns = null;
+ }
+ this.matchPatterns = match.stream().map(r -> Pattern.compile(r, flags))
+ .collect(Collectors.toList());
+ }
+
+ DiffDriver(List<String> match) {
+ this(null, match, 0);
+ }
+
+ DiffDriver(List<String> negate, List<String> match) {
+ this(negate, match, 0);
+ }
+
+ /**
+ * Returns the list of patterns used to exclude certain lines from being
+ * considered as function names.
+ *
+ * @return the list of negate patterns
+ */
+ public List<Pattern> getNegatePatterns() {
+ return negatePatterns;
+ }
+
+ /**
+ * Returns the list of patterns used to match lines for potential function
+ * names.
+ *
+ * @return the list of match patterns
+ */
+ public List<Pattern> getMatchPatterns() {
+ return matchPatterns;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
index 2f472b5c0a..cbac3f90b7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
@@ -30,7 +30,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.regex.Pattern;
import org.eclipse.jgit.api.errors.CanceledException;
+import org.eclipse.jgit.attributes.Attribute;
import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.dircache.DirCacheIterator;
@@ -703,7 +705,7 @@ public class DiffFormatter implements AutoCloseable {
*/
public void format(DiffEntry ent) throws IOException {
FormatResult res = createFormatResult(ent);
- format(res.header, res.a, res.b);
+ format(res.header, res.a, res.b, getDiffDriver(ent));
}
private static byte[] writeGitLinkText(AbbreviatedObjectId id) {
@@ -749,11 +751,14 @@ public class DiffFormatter implements AutoCloseable {
* text source for the post-image version of the content. This
* must match the content of
* {@link org.eclipse.jgit.patch.FileHeader#getNewId()}.
+ * @param diffDriver
+ * the diff driver used to obtain function names in hunk headers
* @throws java.io.IOException
- * writing to the supplied stream failed.
+ * writing to the supplied stream failed.
+ * @since 6.10.1
*/
- public void format(FileHeader head, RawText a, RawText b)
- throws IOException {
+ public void format(FileHeader head, RawText a, RawText b,
+ DiffDriver diffDriver) throws IOException {
// Reuse the existing FileHeader as-is by blindly copying its
// header lines, but avoiding its hunks. Instead we recreate
// the hunks from the text instances we have been supplied.
@@ -763,8 +768,49 @@ public class DiffFormatter implements AutoCloseable {
if (!head.getHunks().isEmpty())
end = head.getHunks().get(0).getStartOffset();
out.write(head.getBuffer(), start, end - start);
- if (head.getPatchType() == PatchType.UNIFIED)
- format(head.toEditList(), a, b);
+ if (head.getPatchType() == PatchType.UNIFIED) {
+ format(head.toEditList(), a, b, diffDriver);
+ }
+ }
+
+ /**
+ * Format a patch script, reusing a previously parsed FileHeader.
+ * <p>
+ * This formatter is primarily useful for editing an existing patch script
+ * to increase or reduce the number of lines of context within the script.
+ * All header lines are reused as-is from the supplied FileHeader.
+ *
+ * @param head
+ * existing file header containing the header lines to copy.
+ * @param a
+ * text source for the pre-image version of the content. This must match
+ * the content of {@link org.eclipse.jgit.patch.FileHeader#getOldId()}.
+ * @param b
+ * text source for the post-image version of the content. This must match
+ * the content of {@link org.eclipse.jgit.patch.FileHeader#getNewId()}.
+ * @throws java.io.IOException
+ * writing to the supplied stream failed.
+ */
+ public void format(FileHeader head, RawText a, RawText b)
+ throws IOException {
+ format(head, a, b, null);
+ }
+
+ /**
+ * Formats a list of edits in unified diff format
+ *
+ * @param edits
+ * some differences which have been calculated between A and B
+ * @param a
+ * the text A which was compared
+ * @param b
+ * the text B which was compared
+ * @throws java.io.IOException
+ * if an IO error occurred
+ */
+ public void format(EditList edits, RawText a, RawText b)
+ throws IOException {
+ format(edits, a, b, null);
}
/**
@@ -776,11 +822,14 @@ public class DiffFormatter implements AutoCloseable {
* the text A which was compared
* @param b
* the text B which was compared
+ * @param diffDriver
+ * the diff driver used to obtain function names in hunk headers
* @throws java.io.IOException
* if an IO error occurred
+ * @since 6.10.1
*/
- public void format(EditList edits, RawText a, RawText b)
- throws IOException {
+ public void format(EditList edits, RawText a, RawText b,
+ DiffDriver diffDriver) throws IOException {
for (int curIdx = 0; curIdx < edits.size();) {
Edit curEdit = edits.get(curIdx);
final int endIdx = findCombinedEnd(edits, curIdx);
@@ -791,7 +840,8 @@ public class DiffFormatter implements AutoCloseable {
final int aEnd = (int) Math.min(a.size(), (long) endEdit.getEndA() + context);
final int bEnd = (int) Math.min(b.size(), (long) endEdit.getEndB() + context);
- writeHunkHeader(aCur, aEnd, bCur, bEnd);
+ writeHunkHeader(aCur, aEnd, bCur, bEnd,
+ getFuncName(a, aCur - 1, diffDriver));
while (aCur < aEnd || bCur < bEnd) {
if (aCur < curEdit.getBeginA() || endIdx + 1 < curIdx) {
@@ -881,8 +931,30 @@ public class DiffFormatter implements AutoCloseable {
* @throws java.io.IOException
* if an IO error occurred
*/
- protected void writeHunkHeader(int aStartLine, int aEndLine,
- int bStartLine, int bEndLine) throws IOException {
+ protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine,
+ int bEndLine) throws IOException {
+ writeHunkHeader(aStartLine, aEndLine, bStartLine, bEndLine, null);
+ }
+
+ /**
+ * Output a hunk header
+ *
+ * @param aStartLine
+ * within first source
+ * @param aEndLine
+ * within first source
+ * @param bStartLine
+ * within second source
+ * @param bEndLine
+ * within second source
+ * @param funcName
+ * function name of this hunk
+ * @throws java.io.IOException
+ * if an IO error occurred
+ * @since 6.10.1
+ */
+ protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine,
+ int bEndLine, String funcName) throws IOException {
out.write('@');
out.write('@');
writeRange('-', aStartLine + 1, aEndLine - aStartLine);
@@ -890,6 +962,10 @@ public class DiffFormatter implements AutoCloseable {
out.write(' ');
out.write('@');
out.write('@');
+ if (funcName != null) {
+ out.write(' ');
+ out.write(funcName.getBytes());
+ }
out.write('\n');
}
@@ -1247,4 +1323,50 @@ public class DiffFormatter implements AutoCloseable {
private static boolean end(Edit edit, int a, int b) {
return edit.getEndA() <= a && edit.getEndB() <= b;
}
+
+ private String getFuncName(RawText text, int startAt,
+ DiffDriver diffDriver) {
+ if (diffDriver != null) {
+ while (startAt > 0) {
+ String line = text.getString(startAt);
+ startAt--;
+ if (matchesAny(diffDriver.getNegatePatterns(), line)) {
+ continue;
+ }
+ if (matchesAny(diffDriver.getMatchPatterns(), line)) {
+ String funcName = line.replaceAll("^[ \\t]+", ""); //$NON-NLS-1$//$NON-NLS-2$
+ return funcName.substring(0,
+ Math.min(funcName.length(), 80)).trim();
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean matchesAny(List<Pattern> patterns, String text) {
+ if (patterns != null) {
+ for (Pattern p : patterns) {
+ if (p.matcher(text).find()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private DiffDriver getDiffDriver(DiffEntry entry) {
+ Attribute diffAttr = entry.getDiffAttribute();
+ if (diffAttr == null) {
+ return null;
+ }
+ String diffAttrValue = diffAttr.getValue();
+ if (diffAttrValue == null) {
+ return null;
+ }
+ try {
+ return DiffDriver.valueOf(diffAttrValue);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java
index 4343642f9a..b401bbe73d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java
@@ -44,8 +44,8 @@ public class PatchIdDiffFormatter extends DiffFormatter {
}
@Override
- protected void writeHunkHeader(int aStartLine, int aEndLine,
- int bStartLine, int bEndLine) throws IOException {
+ protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine,
+ int bEndLine, String funcName) throws IOException {
// The hunk header is not taken into account for patch id calculation
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
index 76dc87e72b..fdfe533618 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
@@ -360,18 +360,22 @@ public class RawText extends Sequence {
length = maxLength;
isComplete = false;
}
- byte last = 'x'; // Just something inconspicuous.
- for (int ptr = 0; ptr < length; ptr++) {
- byte curr = raw[ptr];
- if (isBinary(curr, last)) {
+
+ int ptr = -1;
+ byte current;
+ while (ptr < length - 2) {
+ current = raw[++ptr];
+ if (current == '\0' || (current == '\r' && raw[++ptr] != '\n')) {
return true;
}
- last = curr;
}
- if (isComplete) {
- // Buffer contains everything...
- return last == '\r'; // ... so this must be a lone CR
+
+ if (ptr == length - 2) {
+ // if '\r' be last, then if isComplete then return binary
+ current = raw[++ptr];
+ return current == '\0' || (current == '\r' && isComplete);
}
+
return false;
}
@@ -467,26 +471,30 @@ public class RawText extends Sequence {
*/
public static boolean isCrLfText(byte[] raw, int length, boolean complete) {
boolean has_crlf = false;
- byte last = 'x'; // Just something inconspicuous
- for (int ptr = 0; ptr < length; ptr++) {
- byte curr = raw[ptr];
- if (isBinary(curr, last)) {
+
+ int ptr = -1;
+ byte current;
+ while (ptr < length - 2) {
+ current = raw[++ptr];
+ if (current == '\0') {
return false;
}
- if (curr == '\n' && last == '\r') {
+ if (current == '\r') {
+ if (raw[++ptr] != '\n') {
+ return false;
+ }
has_crlf = true;
}
- last = curr;
}
- if (last == '\r') {
- if (complete) {
- // Lone CR: it's binary after all.
+
+ if (ptr == length - 2) {
+ // if '\r' be last, then if isComplete then return binary
+ current = raw[++ptr];
+ if (current == '\0' || (current == '\r' && complete)) {
return false;
}
- // Tough call. If the next byte, which we don't have, would be a
- // '\n', it'd be a CR-LF text, otherwise it'd be binary. Just decide
- // based on what we already scanned; it wasn't binary until now.
}
+
return has_crlf;
}
@@ -578,4 +586,5 @@ public class RawText extends Sequence {
return new RawText(data, RawParseUtils.lineMapOrBinary(data, 0, (int) sz));
}
}
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
index 5de7bac112..fb98df7c9e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
@@ -80,7 +80,7 @@ class SimilarityRenameDetector {
private long[] matrix;
/** Score a pair must exceed to be considered a rename. */
- private int renameScore = 60;
+ private int renameScore = 50;
/**
* File size threshold (in bytes) for detecting renames. Files larger
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/Checkout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/Checkout.java
index accf732dc7..de02aecdb9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/Checkout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/Checkout.java
@@ -217,10 +217,18 @@ public class Checkout {
}
}
try {
- if (recursiveDelete && Files.isDirectory(f.toPath(),
- LinkOption.NOFOLLOW_LINKS)) {
+ boolean isDir = Files.isDirectory(f.toPath(),
+ LinkOption.NOFOLLOW_LINKS);
+ if (recursiveDelete && isDir) {
FileUtils.delete(f, FileUtils.RECURSIVE);
}
+ if (cache.getRepository().isWorkTreeCaseInsensitive() && !isDir) {
+ // We cannot rely on rename via Files.move() to work correctly
+ // if the target exists in a case variant. For instance with JDK
+ // 17 on Mac OS, the existing case-variant name is kept. On
+ // Windows 11 it would work and use the name given in 'f'.
+ FileUtils.delete(f, FileUtils.SKIP_MISSING);
+ }
FileUtils.rename(tmpFile, f, StandardCopyOption.ATOMIC_MOVE);
cachedParent.remove(f.getName());
} catch (IOException e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
index 34dba0b5be..c650d6e8e7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
@@ -1037,7 +1037,12 @@ public class DirCache {
}
}
- enum DirCacheVersion implements ConfigEnum {
+ /**
+ * DirCache versions
+ *
+ * @since 7.2
+ */
+ public enum DirCacheVersion implements ConfigEnum {
/** Minimum index version on-disk format that we support. */
DIRC_VERSION_MINIMUM(2),
@@ -1060,6 +1065,9 @@ public class DirCache {
this.version = versionCode;
}
+ /**
+ * @return the version code for this version
+ */
public int getVersionCode() {
return version;
}
@@ -1078,6 +1086,13 @@ public class DirCache {
}
}
+ /**
+ * Create DirCacheVersion from integer value of the version code.
+ *
+ * @param val
+ * integer value of the version code.
+ * @return the DirCacheVersion instance of the version code.
+ */
public static DirCacheVersion fromInt(int val) {
for (DirCacheVersion v : DirCacheVersion.values()) {
if (val == v.getVersionCode()) {
@@ -1098,9 +1113,8 @@ public class DirCache {
boolean manyFiles = cfg.getBoolean(
ConfigConstants.CONFIG_FEATURE_SECTION,
ConfigConstants.CONFIG_KEY_MANYFILES, false);
- indexVersion = cfg.getEnum(DirCacheVersion.values(),
- ConfigConstants.CONFIG_INDEX_SECTION, null,
- ConfigConstants.CONFIG_KEY_VERSION,
+ indexVersion = cfg.getEnum(ConfigConstants.CONFIG_INDEX_SECTION,
+ null, ConfigConstants.CONFIG_KEY_VERSION,
manyFiles ? DirCacheVersion.DIRC_VERSION_PATHCOMPRESS
: DirCacheVersion.DIRC_VERSION_EXTENDED);
skipHash = cfg.getBoolean(ConfigConstants.CONFIG_INDEX_SECTION,
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 6ae5153c12..18d77482e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -5,7 +5,7 @@
* Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org>
* Copyright (C) 2010, Chrisian Halstrick <christian.halstrick@sap.com>
* Copyright (C) 2019, 2020, Andre Bossert <andre.bossert@siemens.com>
- * Copyright (C) 2017, 2023, Thomas Wolf <twolf@apache.org> and others
+ * Copyright (C) 2017, 2025, Thomas Wolf <twolf@apache.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,6 +31,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeSet;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.api.errors.FilterFailedException;
@@ -66,7 +67,6 @@ import org.eclipse.jgit.treewalk.WorkingTreeOptions;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FS.ExecutionResult;
-import org.eclipse.jgit.util.IntList;
import org.eclipse.jgit.util.SystemReader;
import org.eclipse.jgit.util.io.EolStreamTypeUtil;
import org.slf4j.Logger;
@@ -113,9 +113,11 @@ public class DirCacheCheckout {
private Map<String, CheckoutMetadata> updated = new LinkedHashMap<>();
+ private Set<String> existing;
+
private ArrayList<String> conflicts = new ArrayList<>();
- private ArrayList<String> removed = new ArrayList<>();
+ private TreeSet<String> removed;
private ArrayList<String> kept = new ArrayList<>();
@@ -185,7 +187,7 @@ public class DirCacheCheckout {
* @return a list of all files removed by this checkout
*/
public List<String> getRemoved() {
- return removed;
+ return new ArrayList<>(removed);
}
/**
@@ -214,6 +216,14 @@ public class DirCacheCheckout {
this.mergeCommitTree = mergeCommitTree;
this.workingTree = workingTree;
this.initialCheckout = !repo.isBare() && !repo.getIndexFile().exists();
+ boolean caseInsensitive = !repo.isBare()
+ && repo.isWorkTreeCaseInsensitive();
+ this.removed = caseInsensitive
+ ? new TreeSet<>(String::compareToIgnoreCase)
+ : new TreeSet<>();
+ this.existing = caseInsensitive
+ ? new TreeSet<>(String::compareToIgnoreCase)
+ : null;
}
/**
@@ -400,9 +410,11 @@ public class DirCacheCheckout {
// content to be checked out.
update(m);
}
- } else
+ } else {
update(m);
- } else if (f == null || !m.idEqual(i)) {
+ }
+ } else if (f == null || !m.idEqual(i)
+ || m.getEntryRawMode() != i.getEntryRawMode()) {
// The working tree file is missing or the merge content differs
// from index content
update(m);
@@ -410,11 +422,11 @@ public class DirCacheCheckout {
// The index contains a file (and not a folder)
if (f.isModified(i.getDirCacheEntry(), true,
this.walk.getObjectReader())
- || i.getDirCacheEntry().getStage() != 0)
+ || i.getDirCacheEntry().getStage() != 0) {
// The working tree file is dirty or the index contains a
// conflict
update(m);
- else {
+ } else {
// update the timestamp of the index with the one from the
// file if not set, as we are sure to be in sync here.
DirCacheEntry entry = i.getDirCacheEntry();
@@ -424,9 +436,10 @@ public class DirCacheCheckout {
}
keep(i.getEntryPathString(), entry, f);
}
- } else
+ } else {
// The index contains a folder
keep(i.getEntryPathString(), i.getDirCacheEntry(), f);
+ }
} else {
// There is no entry in the merge commit. Means: we want to delete
// what's currently in the index and working tree
@@ -521,6 +534,13 @@ public class DirCacheCheckout {
// update our index
builder.finish();
+ // On case-insensitive file systems we may have a case variant kept
+ // and another one removed. In that case, don't remove it.
+ if (existing != null) {
+ removed.removeAll(existing);
+ existing.clear();
+ }
+
// init progress reporting
int numTotal = removed.size() + updated.size() + conflicts.size();
monitor.beginTask(JGitText.get().checkingOutFiles, numTotal);
@@ -531,9 +551,9 @@ public class DirCacheCheckout {
// when deleting files process them in the opposite order as they have
// been reported. This ensures the files are deleted before we delete
// their parent folders
- IntList nonDeleted = new IntList();
- for (int i = removed.size() - 1; i >= 0; i--) {
- String r = removed.get(i);
+ Iterator<String> iter = removed.descendingIterator();
+ while (iter.hasNext()) {
+ String r = iter.next();
file = new File(repo.getWorkTree(), r);
if (!file.delete() && repo.getFS().exists(file)) {
// The list of stuff to delete comes from the index
@@ -542,7 +562,7 @@ public class DirCacheCheckout {
// to delete it. A submodule is not empty, so it
// is safe to check this after a failed delete.
if (!repo.getFS().isDirectory(file)) {
- nonDeleted.add(i);
+ iter.remove();
toBeDeleted.add(r);
}
} else {
@@ -560,8 +580,6 @@ public class DirCacheCheckout {
if (file != null) {
removeEmptyParents(file);
}
- removed = filterOut(removed, nonDeleted);
- nonDeleted = null;
Iterator<Map.Entry<String, CheckoutMetadata>> toUpdate = updated
.entrySet().iterator();
Map.Entry<String, CheckoutMetadata> e = null;
@@ -633,36 +651,6 @@ public class DirCacheCheckout {
return toBeDeleted.isEmpty();
}
- private static ArrayList<String> filterOut(ArrayList<String> strings,
- IntList indicesToRemove) {
- int n = indicesToRemove.size();
- if (n == strings.size()) {
- return new ArrayList<>(0);
- }
- switch (n) {
- case 0:
- return strings;
- case 1:
- strings.remove(indicesToRemove.get(0));
- return strings;
- default:
- int length = strings.size();
- ArrayList<String> result = new ArrayList<>(length - n);
- // Process indicesToRemove from the back; we know that it
- // contains indices in descending order.
- int j = n - 1;
- int idx = indicesToRemove.get(j);
- for (int i = 0; i < length; i++) {
- if (i == idx) {
- idx = (--j >= 0) ? indicesToRemove.get(j) : -1;
- } else {
- result.add(strings.get(i));
- }
- }
- return result;
- }
- }
-
private static boolean isSamePrefix(String a, String b) {
int as = a.lastIndexOf('/');
int bs = b.lastIndexOf('/');
@@ -1233,6 +1221,9 @@ public class DirCacheCheckout {
if (!FileMode.TREE.equals(e.getFileMode())) {
builder.add(e);
}
+ if (existing != null) {
+ existing.add(path);
+ }
if (force) {
if (f == null || f.isModified(e, true, walk.getObjectReader())) {
kept.add(path);
@@ -1401,127 +1392,6 @@ public class DirCacheCheckout {
}
/**
- * Updates the file in the working tree with content and mode from an entry
- * in the index. The new content is first written to a new temporary file in
- * the same directory as the real file. Then that new file is renamed to the
- * final filename.
- *
- * <p>
- * <b>Note:</b> if the entry path on local file system exists as a non-empty
- * directory, and the target entry type is a link or file, the checkout will
- * fail with {@link java.io.IOException} since existing non-empty directory
- * cannot be renamed to file or link without deleting it recursively.
- * </p>
- *
- * @param repo
- * repository managing the destination work tree.
- * @param entry
- * the entry containing new mode and content
- * @param or
- * object reader to use for checkout
- * @throws java.io.IOException
- * if an IO error occurred
- * @since 3.6
- * @deprecated since 5.1, use
- * {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata, WorkingTreeOptions)}
- * instead
- */
- @Deprecated
- public static void checkoutEntry(Repository repo, DirCacheEntry entry,
- ObjectReader or) throws IOException {
- checkoutEntry(repo, entry, or, false, null, null);
- }
-
-
- /**
- * Updates the file in the working tree with content and mode from an entry
- * in the index. The new content is first written to a new temporary file in
- * the same directory as the real file. Then that new file is renamed to the
- * final filename.
- *
- * <p>
- * <b>Note:</b> if the entry path on local file system exists as a file, it
- * will be deleted and if it exists as a directory, it will be deleted
- * recursively, independently if has any content.
- * </p>
- *
- * @param repo
- * repository managing the destination work tree.
- * @param entry
- * the entry containing new mode and content
- * @param or
- * object reader to use for checkout
- * @param deleteRecursive
- * true to recursively delete final path if it exists on the file
- * system
- * @param checkoutMetadata
- * containing
- * <ul>
- * <li>smudgeFilterCommand to be run for smudging the entry to be
- * checked out</li>
- * <li>eolStreamType used for stream conversion</li>
- * </ul>
- * @throws java.io.IOException
- * if an IO error occurred
- * @since 4.2
- * @deprecated since 6.3, use
- * {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata, WorkingTreeOptions)}
- * instead
- */
- @Deprecated
- public static void checkoutEntry(Repository repo, DirCacheEntry entry,
- ObjectReader or, boolean deleteRecursive,
- CheckoutMetadata checkoutMetadata) throws IOException {
- checkoutEntry(repo, entry, or, deleteRecursive, checkoutMetadata, null);
- }
-
- /**
- * Updates the file in the working tree with content and mode from an entry
- * in the index. The new content is first written to a new temporary file in
- * the same directory as the real file. Then that new file is renamed to the
- * final filename.
- *
- * <p>
- * <b>Note:</b> if the entry path on local file system exists as a file, it
- * will be deleted and if it exists as a directory, it will be deleted
- * recursively, independently if has any content.
- * </p>
- *
- * @param repo
- * repository managing the destination work tree.
- * @param entry
- * the entry containing new mode and content
- * @param or
- * object reader to use for checkout
- * @param deleteRecursive
- * true to recursively delete final path if it exists on the file
- * system
- * @param checkoutMetadata
- * containing
- * <ul>
- * <li>smudgeFilterCommand to be run for smudging the entry to be
- * checked out</li>
- * <li>eolStreamType used for stream conversion</li>
- * </ul>
- * @param options
- * {@link WorkingTreeOptions} that are effective; if {@code null}
- * they are loaded from the repository config
- * @throws java.io.IOException
- * if an IO error occurred
- * @since 6.3
- * @deprecated since 6.6.1; use {@link Checkout} instead
- */
- @Deprecated
- public static void checkoutEntry(Repository repo, DirCacheEntry entry,
- ObjectReader or, boolean deleteRecursive,
- CheckoutMetadata checkoutMetadata, WorkingTreeOptions options)
- throws IOException {
- Checkout checkout = new Checkout(repo, options)
- .setRecursiveDeletion(deleteRecursive);
- checkout.checkout(entry, checkoutMetadata, or, null);
- }
-
- /**
* Return filtered content for a specific object (blob). EOL handling and
* smudge-filter handling are applied in the same way as it would be done
* during a checkout.
@@ -1647,6 +1517,8 @@ public class DirCacheCheckout {
filterProcessBuilder.directory(repo.getWorkTree());
filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
repo.getDirectory().getAbsolutePath());
+ filterProcessBuilder.environment().put(Constants.GIT_COMMON_DIR_KEY,
+ repo.getCommonDirectory().getAbsolutePath());
ExecutionResult result;
int rc;
try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
index c5e1e4e765..5a22938694 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
@@ -396,28 +396,6 @@ public class DirCacheEntry {
* timestamp. This method tests to see if file was written out at the same
* time as the index.
*
- * @param smudge_s
- * seconds component of the index's last modified time.
- * @param smudge_ns
- * nanoseconds component of the index's last modified time.
- * @return true if extra careful checks should be used.
- * @deprecated use {@link #mightBeRacilyClean(Instant)} instead
- */
- @Deprecated
- public final boolean mightBeRacilyClean(int smudge_s, int smudge_ns) {
- return mightBeRacilyClean(Instant.ofEpochSecond(smudge_s, smudge_ns));
- }
-
- /**
- * Is it possible for this entry to be accidentally assumed clean?
- * <p>
- * The "racy git" problem happens when a work file can be updated faster
- * than the filesystem records file modification timestamps. It is possible
- * for an application to edit a work file, update the index, then edit it
- * again before the filesystem will give the work file a new modification
- * timestamp. This method tests to see if file was written out at the same
- * time as the index.
- *
* @param smudge
* index's last modified time.
* @return true if extra careful checks should be used.
@@ -653,22 +631,6 @@ public class DirCacheEntry {
}
/**
- * Get the cached last modification date of this file, in milliseconds.
- * <p>
- * One of the indicators that the file has been modified by an application
- * changing the working tree is if the last modification time for the file
- * differs from the time stored in this entry.
- *
- * @return last modification time of this file, in milliseconds since the
- * Java epoch (midnight Jan 1, 1970 UTC).
- * @deprecated use {@link #getLastModifiedInstant()} instead
- */
- @Deprecated
- public long getLastModified() {
- return decodeTS(P_MTIME);
- }
-
- /**
* Get the cached last modification date of this file.
* <p>
* One of the indicators that the file has been modified by an application
@@ -683,18 +645,6 @@ public class DirCacheEntry {
}
/**
- * Set the cached last modification date of this file, using milliseconds.
- *
- * @param when
- * new cached modification date of the file, in milliseconds.
- * @deprecated use {@link #setLastModified(Instant)} instead
- */
- @Deprecated
- public void setLastModified(long when) {
- encodeTS(P_MTIME, when);
- }
-
- /**
* Set the cached last modification date of this file.
*
* @param when
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 1fd80867b9..38982fdf23 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java
@@ -23,18 +23,6 @@ public class PackInvalidException extends IOException {
private static final long serialVersionUID = 1L;
/**
- * Construct a pack invalid error.
- *
- * @param path
- * path of the invalid pack file.
- * @deprecated Use {@link #PackInvalidException(File, Throwable)}.
- */
- @Deprecated
- public PackInvalidException(File path) {
- this(path, null);
- }
-
- /**
* Construct a pack invalid error with cause.
*
* @param path
@@ -48,18 +36,6 @@ public class PackInvalidException extends IOException {
}
/**
- * Construct a pack invalid error.
- *
- * @param path
- * path of the invalid pack file.
- * @deprecated Use {@link #PackInvalidException(String, Throwable)}.
- */
- @Deprecated
- public PackInvalidException(String path) {
- this(path, null);
- }
-
- /**
* Construct a pack invalid error with cause.
*
* @param path
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java
index 3ce97a4ff7..e511a68d2e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java
@@ -156,6 +156,9 @@ class BareSuperprojectWriter {
ObjectId objectId;
if (ObjectId.isId(proj.getRevision())) {
objectId = ObjectId.fromString(proj.getRevision());
+ if (config.recordRemoteBranch && proj.getUpstream() != null) {
+ cfg.setString("submodule", name, "ref", proj.getUpstream()); //$NON-NLS-1$//$NON-NLS-2$
+ }
} else {
objectId = callback.sha1(url, proj.getRevision());
if (objectId == null && !config.ignoreRemoteFailures) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
index 957b3869f2..b033177e05 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
@@ -176,6 +176,10 @@ public class ManifestParser extends DefaultHandler {
attributes.getValue("groups"));
currentProject
.setRecommendShallow(attributes.getValue("clone-depth"));
+ currentProject
+ .setUpstream(attributes.getValue("upstream"));
+ currentProject
+ .setDestBranch(attributes.getValue("dest-branch"));
break;
case "remote":
String alias = attributes.getValue("alias");
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index 95c1c8b22e..be77fca459 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -111,32 +111,6 @@ public class RepoCommand extends GitCommand<RevCommit> {
public ObjectId sha1(String uri, String ref) throws GitAPIException;
/**
- * Read a file from a remote repository.
- *
- * @param uri
- * The URI of the remote repository
- * @param ref
- * The ref (branch/tag/etc.) to read
- * @param path
- * The relative path (inside the repo) to the file to read
- * @return the file content.
- * @throws GitAPIException
- * If the ref have an invalid or ambiguous name, or it does
- * not exist in the repository,
- * @throws IOException
- * If the object does not exist or is too large
- * @since 3.5
- *
- * @deprecated Use {@link #readFileWithMode(String, String, String)}
- * instead
- */
- @Deprecated
- public default byte[] readFile(String uri, String ref, String path)
- throws GitAPIException, IOException {
- return readFileWithMode(uri, ref, path).getContents();
- }
-
- /**
* Read contents and mode (i.e. permissions) of the file from a remote
* repository.
*
@@ -255,7 +229,8 @@ public class RepoCommand extends GitCommand<RevCommit> {
@SuppressWarnings("serial")
static class ManifestErrorException extends GitAPIException {
ManifestErrorException(Throwable cause) {
- super(RepoText.get().invalidManifest, cause);
+ super(RepoText.get().invalidManifest + " " + cause.getMessage(), //$NON-NLS-1$
+ cause);
}
}
@@ -615,6 +590,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
p.setUrl(proj.getUrl());
p.addCopyFiles(proj.getCopyFiles());
p.addLinkFiles(proj.getLinkFiles());
+ p.setUpstream(proj.getUpstream());
ret.add(p);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
index 8deb7386a6..2630da34e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
@@ -38,6 +38,8 @@ public class RepoProject implements Comparable<RepoProject> {
private final Set<String> groups;
private final List<CopyFile> copyfiles;
private final List<LinkFile> linkfiles;
+ private String upstream;
+ private String destBranch;
private String recommendShallow;
private String url;
private String defaultRevision;
@@ -389,6 +391,57 @@ public class RepoProject implements Comparable<RepoProject> {
this.linkfiles.clear();
}
+ /**
+ * Return the upstream attribute of the project
+ *
+ * @return the upstream value if present, null otherwise.
+ *
+ * @since 6.10
+ */
+ public String getUpstream() {
+ return this.upstream;
+ }
+
+ /**
+ * Return the dest-branch attribute of the project
+ *
+ * @return the dest-branch value if present, null otherwise.
+ *
+ * @since 6.10
+ */
+ public String getDestBranch() {
+ return this.destBranch;
+ }
+
+ /**
+ * Set the upstream attribute of the project
+ *
+ * Name of the git ref in which a sha1 can be found, when the revision is a
+ * sha1.
+ *
+ * @param upstream
+ * value of the attribute in the manifest
+ *
+ * @since 6.10
+ */
+ public void setUpstream(String upstream) {
+ this.upstream = upstream;
+ }
+
+ /**
+ * Set the dest-branch attribute of the project
+ *
+ * Name of a Git branch.
+ *
+ * @param destBranch
+ * value of the attribute in the manifest
+ *
+ * @since 6.10
+ */
+ public void setDestBranch(String destBranch) {
+ this.destBranch = destBranch;
+ }
+
private String getPathWithSlash() {
if (path.endsWith("/")) { //$NON-NLS-1$
return path;
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 700b54a7a6..bf252f9968 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -94,8 +94,6 @@ public class JGitText extends TranslationBundle {
/***/ public String binaryHunkInvalidLength;
/***/ public String binaryHunkLineTooShort;
/***/ public String binaryHunkMissingNewline;
- /***/ public String bitmapAccessErrorForPackfile;
- /***/ public String bitmapFailedToGet;
/***/ public String bitmapMissingObject;
/***/ public String bitmapsMustBePrepared;
/***/ public String bitmapUseNoopNoListener;
@@ -108,6 +106,7 @@ public class JGitText extends TranslationBundle {
/***/ public String buildingBitmaps;
/***/ public String cachedPacksPreventsIndexCreation;
/***/ public String cachedPacksPreventsListingObjects;
+ /***/ public String cacheRegionAllOrNoneNull;
/***/ public String cannotAccessLastModifiedForSafeDeletion;
/***/ public String cannotBeCombined;
/***/ public String cannotBeRecursiveWhenTreesAreIncluded;
@@ -295,6 +294,7 @@ public class JGitText extends TranslationBundle {
/***/ public String deleteTagUnexpectedResult;
/***/ public String deletingBranches;
/***/ public String deletingNotSupported;
+ /***/ public String deprecatedTrustFolderStat;
/***/ public String depthMustBeAt1;
/***/ public String depthWithUnshallow;
/***/ public String destinationIsNotAWildcard;
@@ -315,6 +315,9 @@ public class JGitText extends TranslationBundle {
/***/ public String downloadCancelled;
/***/ public String downloadCancelledDuringIndexing;
/***/ public String duplicateAdvertisementsOf;
+ /***/ public String duplicateCacheTablesGiven;
+ /***/ public String duplicatePackExtensionsForCacheTables;
+ /***/ public String duplicatePackExtensionsSet;
/***/ public String duplicateRef;
/***/ public String duplicateRefAttribute;
/***/ public String duplicateRemoteRefUpdateIsIllegal;
@@ -490,6 +493,7 @@ public class JGitText extends TranslationBundle {
/***/ public String invalidTimeUnitValue2;
/***/ public String invalidTimeUnitValue3;
/***/ public String invalidTreeZeroLengthName;
+ /***/ public String invalidTrustStat;
/***/ public String invalidURL;
/***/ public String invalidWildcards;
/***/ public String invalidRefSpec;
@@ -554,6 +558,8 @@ public class JGitText extends TranslationBundle {
/***/ public String month;
/***/ public String months;
/***/ public String monthsAgo;
+ /***/ public String multiPackIndexUnexpectedSize;
+ /***/ public String multiPackIndexWritingCancelled;
/***/ public String multipleMergeBasesFor;
/***/ public String nameMustNotBeNullOrEmpty;
/***/ public String need2Arguments;
@@ -569,6 +575,8 @@ public class JGitText extends TranslationBundle {
/***/ public String noMergeHeadSpecified;
/***/ public String nonBareLinkFilesNotSupported;
/***/ public String nonCommitToHeads;
+ /***/ public String noPackExtConfigurationGiven;
+ /***/ public String noPackExtGivenForConfiguration;
/***/ public String noPathAttributesFound;
/***/ public String noSuchRef;
/***/ public String noSuchRefKnown;
@@ -601,7 +609,6 @@ public class JGitText extends TranslationBundle {
/***/ public String oldIdMustNotBeNull;
/***/ public String onlyOneFetchSupported;
/***/ public String onlyOneOperationCallPerConnectionIsSupported;
- /***/ public String onlyOpenPgpSupportedForSigning;
/***/ public String openFilesMustBeAtLeast1;
/***/ public String openingConnection;
/***/ public String operationCanceled;
@@ -623,6 +630,8 @@ public class JGitText extends TranslationBundle {
/***/ public String packingCancelledDuringObjectsWriting;
/***/ public String packObjectCountMismatch;
/***/ public String packRefs;
+ /***/ public String packRefsFailed;
+ /***/ public String packRefsSuccessful;
/***/ public String packSizeNotSetYet;
/***/ public String packTooLargeForIndexVersion1;
/***/ public String packWasDeleted;
@@ -639,6 +648,7 @@ public class JGitText extends TranslationBundle {
/***/ public String personIdentEmailNonNull;
/***/ public String personIdentNameNonNull;
/***/ public String postCommitHookFailed;
+ /***/ public String precedenceTrustConfig;
/***/ public String prefixRemote;
/***/ public String problemWithResolvingPushRefSpecsLocally;
/***/ public String progressMonUploading;
@@ -666,8 +676,6 @@ public class JGitText extends TranslationBundle {
/***/ public String readerIsRequired;
/***/ public String readingObjectsFromLocalRepositoryFailed;
/***/ public String readLastModifiedFailed;
- /***/ public String readPipeIsNotAllowed;
- /***/ public String readPipeIsNotAllowedRequiredPermission;
/***/ public String readTimedOut;
/***/ public String receivePackObjectTooLarge1;
/***/ public String receivePackObjectTooLarge2;
@@ -747,6 +755,8 @@ public class JGitText extends TranslationBundle {
/***/ public String shutdownCleanup;
/***/ public String shutdownCleanupFailed;
/***/ public String shutdownCleanupListenerFailed;
+ /***/ public String signatureServiceConflict;
+ /***/ public String signatureTypeUnknown;
/***/ public String signatureVerificationError;
/***/ public String signatureVerificationUnavailable;
/***/ public String signedTagMessageNoLf;
@@ -833,6 +843,7 @@ public class JGitText extends TranslationBundle {
/***/ public String unableToCheckConnectivity;
/***/ public String unableToCreateNewObject;
/***/ public String unableToReadFullInt;
+ /***/ public String unableToReadFullArray;
/***/ public String unableToReadPackfile;
/***/ public String unableToRemovePath;
/***/ public String unableToWrite;
@@ -858,6 +869,7 @@ public class JGitText extends TranslationBundle {
/***/ public String unknownObjectInIndex;
/***/ public String unknownObjectType;
/***/ public String unknownObjectType2;
+ /***/ public String unknownPackExtension;
/***/ public String unknownPositionEncoding;
/***/ public String unknownRefStorageFormat;
/***/ public String unknownRepositoryFormat;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriter.java
index 0d9815eceb..55539e2a66 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriter.java
@@ -52,6 +52,7 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.eclipse.jgit.util.NB;
/**
@@ -71,6 +72,9 @@ public class CommitGraphWriter {
private static final int MAX_CHANGED_PATHS = 512;
+ private static final PathDiffCalculator PATH_DIFF_CALCULATOR
+ = new PathDiffCalculator();
+
private final int hashsz;
private final GraphCommits graphCommits;
@@ -374,37 +378,6 @@ public class CommitGraphWriter {
return generations;
}
- private static Optional<HashSet<ByteBuffer>> computeBloomFilterPaths(
- ObjectReader or, RevCommit cmit) throws MissingObjectException,
- IncorrectObjectTypeException, CorruptObjectException, IOException {
- HashSet<ByteBuffer> paths = new HashSet<>();
- try (TreeWalk walk = new TreeWalk(null, or)) {
- walk.setRecursive(true);
- if (cmit.getParentCount() == 0) {
- walk.addTree(new EmptyTreeIterator());
- } else {
- walk.addTree(cmit.getParent(0).getTree());
- }
- walk.addTree(cmit.getTree());
- while (walk.next()) {
- if (walk.idEqual(0, 1)) {
- continue;
- }
- byte[] rawPath = walk.getRawPath();
- paths.add(ByteBuffer.wrap(rawPath));
- for (int i = 0; i < rawPath.length; i++) {
- if (rawPath[i] == '/') {
- paths.add(ByteBuffer.wrap(rawPath, 0, i));
- }
- if (paths.size() > MAX_CHANGED_PATHS) {
- return Optional.empty();
- }
- }
- }
- }
- return Optional.of(paths);
- }
-
private BloomFilterChunks computeBloomFilterChunks(ProgressMonitor monitor)
throws MissingObjectException, IncorrectObjectTypeException,
CorruptObjectException, IOException {
@@ -435,8 +408,8 @@ public class CommitGraphWriter {
filtersReused++;
} else {
filtersComputed++;
- Optional<HashSet<ByteBuffer>> paths = computeBloomFilterPaths(
- graphCommits.getObjectReader(), cmit);
+ Optional<HashSet<ByteBuffer>> paths = PATH_DIFF_CALCULATOR
+ .changedPaths(graphCommits.getObjectReader(), cmit);
if (paths.isEmpty()) {
cpf = ChangedPathFilter.FULL;
} else {
@@ -473,6 +446,44 @@ public class CommitGraphWriter {
}
}
+ // Visible for testing
+ static class PathDiffCalculator {
+
+ // Walk steps in the last invocation of changedPaths
+ int stepCounter;
+
+ Optional<HashSet<ByteBuffer>> changedPaths(
+ ObjectReader or, RevCommit cmit) throws MissingObjectException,
+ IncorrectObjectTypeException, CorruptObjectException, IOException {
+ stepCounter = 0;
+ HashSet<ByteBuffer> paths = new HashSet<>();
+ try (TreeWalk walk = new TreeWalk(null, or)) {
+ walk.setRecursive(true);
+ walk.setFilter(TreeFilter.ANY_DIFF);
+ if (cmit.getParentCount() == 0) {
+ walk.addTree(new EmptyTreeIterator());
+ } else {
+ walk.addTree(cmit.getParent(0).getTree());
+ }
+ walk.addTree(cmit.getTree());
+ while (walk.next()) {
+ stepCounter += 1;
+ byte[] rawPath = walk.getRawPath();
+ paths.add(ByteBuffer.wrap(rawPath));
+ for (int i = 0; i < rawPath.length; i++) {
+ if (rawPath[i] == '/') {
+ paths.add(ByteBuffer.wrap(rawPath, 0, i));
+ }
+ if (paths.size() > MAX_CHANGED_PATHS) {
+ return Optional.empty();
+ }
+ }
+ }
+ }
+ return Optional.of(paths);
+ }
+ }
+
private static class ChunkHeader {
final int id;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStats.java
new file mode 100644
index 0000000000..295b702fa7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStats.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2024, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheTable.BlockCacheStats;
+
+import java.util.List;
+
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+
+/**
+ * Aggregates values for all given {@link BlockCacheStats}.
+ */
+class AggregatedBlockCacheStats implements BlockCacheStats {
+ private final List<BlockCacheStats> blockCacheStats;
+
+ static BlockCacheStats fromStatsList(
+ List<BlockCacheStats> blockCacheStats) {
+ if (blockCacheStats.size() == 1) {
+ return blockCacheStats.get(0);
+ }
+ return new AggregatedBlockCacheStats(blockCacheStats);
+ }
+
+ private AggregatedBlockCacheStats(List<BlockCacheStats> blockCacheStats) {
+ this.blockCacheStats = blockCacheStats;
+ }
+
+ @Override
+ public String getName() {
+ return AggregatedBlockCacheStats.class.getName();
+ }
+
+ @Override
+ public long[] getCurrentSize() {
+ long[] sums = emptyPackStats();
+ for (BlockCacheStats blockCacheStatsEntry : blockCacheStats) {
+ sums = add(sums, blockCacheStatsEntry.getCurrentSize());
+ }
+ return sums;
+ }
+
+ @Override
+ public long[] getHitCount() {
+ long[] sums = emptyPackStats();
+ for (BlockCacheStats blockCacheStatsEntry : blockCacheStats) {
+ sums = add(sums, blockCacheStatsEntry.getHitCount());
+ }
+ return sums;
+ }
+
+ @Override
+ public long[] getMissCount() {
+ long[] sums = emptyPackStats();
+ for (BlockCacheStats blockCacheStatsEntry : blockCacheStats) {
+ sums = add(sums, blockCacheStatsEntry.getMissCount());
+ }
+ return sums;
+ }
+
+ @Override
+ public long[] getTotalRequestCount() {
+ long[] sums = emptyPackStats();
+ for (BlockCacheStats blockCacheStatsEntry : blockCacheStats) {
+ sums = add(sums, blockCacheStatsEntry.getTotalRequestCount());
+ }
+ return sums;
+ }
+
+ @Override
+ public long[] getHitRatio() {
+ long[] hit = getHitCount();
+ long[] miss = getMissCount();
+ long[] ratio = new long[Math.max(hit.length, miss.length)];
+ for (int i = 0; i < ratio.length; i++) {
+ if (i >= hit.length) {
+ ratio[i] = 0;
+ } else if (i >= miss.length) {
+ ratio[i] = 100;
+ } else {
+ long total = hit[i] + miss[i];
+ ratio[i] = total == 0 ? 0 : hit[i] * 100 / total;
+ }
+ }
+ return ratio;
+ }
+
+ @Override
+ public long[] getEvictions() {
+ long[] sums = emptyPackStats();
+ for (BlockCacheStats blockCacheStatsEntry : blockCacheStats) {
+ sums = add(sums, blockCacheStatsEntry.getEvictions());
+ }
+ return sums;
+ }
+
+ private static long[] emptyPackStats() {
+ return new long[PackExt.values().length];
+ }
+
+ private static long[] add(long[] first, long[] second) {
+ long[] sums = new long[Integer.max(first.length, second.length)];
+ int i;
+ for (i = 0; i < Integer.min(first.length, second.length); i++) {
+ sums[i] = first[i] + second[i];
+ }
+ for (int j = i; j < first.length; j++) {
+ sums[j] = first[i];
+ }
+ for (int j = i; j < second.length; j++) {
+ sums[j] = second[i];
+ }
+ return sums;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTable.java
index d0907bcc8d..587d482583 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTable.java
@@ -12,6 +12,7 @@ package org.eclipse.jgit.internal.storage.dfs;
import java.io.IOException;
import java.time.Duration;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReferenceArray;
@@ -47,6 +48,11 @@ import org.eclipse.jgit.internal.storage.pack.PackExt;
* invocations is also fixed in size.
*/
final class ClockBlockCacheTable implements DfsBlockCacheTable {
+ /**
+ * Table name.
+ */
+ private final String name;
+
/** Number of entries in {@link #table}. */
private final int tableSize;
@@ -129,14 +135,20 @@ final class ClockBlockCacheTable implements DfsBlockCacheTable {
-1, 0, null);
clockHand.next = clockHand;
- this.dfsBlockCacheStats = new DfsBlockCacheStats();
+ this.name = cfg.getName();
+ this.dfsBlockCacheStats = new DfsBlockCacheStats(this.name);
this.refLockWaitTime = cfg.getRefLockWaitTimeConsumer();
this.indexEventConsumer = cfg.getIndexEventConsumer();
}
@Override
- public DfsBlockCacheStats getDfsBlockCacheStats() {
- return dfsBlockCacheStats;
+ public List<BlockCacheStats> getBlockCacheStats() {
+ return List.of(dfsBlockCacheStats);
+ }
+
+ @Override
+ public String getName() {
+ return name;
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
index 56719cf0f4..f8e0831e1f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
@@ -11,7 +11,10 @@
package org.eclipse.jgit.internal.storage.dfs;
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheTable.BlockCacheStats;
+
import java.io.IOException;
+import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.LongStream;
@@ -97,7 +100,12 @@ public final class DfsBlockCache {
double streamRatio = cfg.getStreamRatio();
maxStreamThroughCache = (long) (maxBytes * streamRatio);
- dfsBlockCacheTable = new ClockBlockCacheTable(cfg);
+ if (!cfg.getPackExtCacheConfigurations().isEmpty()) {
+ dfsBlockCacheTable = PackExtBlockCacheTable
+ .fromBlockCacheConfigs(cfg);
+ } else {
+ dfsBlockCacheTable = new ClockBlockCacheTable(cfg);
+ }
for (int i = 0; i < PackExt.values().length; ++i) {
Integer limit = cfg.getCacheHotMap().get(PackExt.values()[i]);
@@ -119,7 +127,7 @@ public final class DfsBlockCache {
* @return total number of bytes in the cache, per pack file extension.
*/
public long[] getCurrentSize() {
- return dfsBlockCacheTable.getDfsBlockCacheStats().getCurrentSize();
+ return getAggregatedBlockCacheStats().getCurrentSize();
}
/**
@@ -138,7 +146,7 @@ public final class DfsBlockCache {
* extension.
*/
public long[] getHitCount() {
- return dfsBlockCacheTable.getDfsBlockCacheStats().getHitCount();
+ return getAggregatedBlockCacheStats().getHitCount();
}
/**
@@ -149,7 +157,7 @@ public final class DfsBlockCache {
* extension.
*/
public long[] getMissCount() {
- return dfsBlockCacheTable.getDfsBlockCacheStats().getMissCount();
+ return getAggregatedBlockCacheStats().getMissCount();
}
/**
@@ -158,8 +166,7 @@ public final class DfsBlockCache {
* @return total number of requests (hit + miss), per pack file extension.
*/
public long[] getTotalRequestCount() {
- return dfsBlockCacheTable.getDfsBlockCacheStats()
- .getTotalRequestCount();
+ return getAggregatedBlockCacheStats().getTotalRequestCount();
}
/**
@@ -168,7 +175,7 @@ public final class DfsBlockCache {
* @return hit ratios
*/
public long[] getHitRatio() {
- return dfsBlockCacheTable.getDfsBlockCacheStats().getHitRatio();
+ return getAggregatedBlockCacheStats().getHitRatio();
}
/**
@@ -179,7 +186,18 @@ public final class DfsBlockCache {
* file extension.
*/
public long[] getEvictions() {
- return dfsBlockCacheTable.getDfsBlockCacheStats().getEvictions();
+ return getAggregatedBlockCacheStats().getEvictions();
+ }
+
+ /**
+ * Get the list of {@link BlockCacheStats} for all underlying caches.
+ * <p>
+ * Useful in monitoring caches with breakdown.
+ *
+ * @return the list of {@link BlockCacheStats} for all underlying caches.
+ */
+ public List<BlockCacheStats> getAllBlockCacheStats() {
+ return dfsBlockCacheTable.getBlockCacheStats();
}
/**
@@ -259,6 +277,11 @@ public final class DfsBlockCache {
return dfsBlockCacheTable.get(key, position);
}
+ private BlockCacheStats getAggregatedBlockCacheStats() {
+ return AggregatedBlockCacheStats
+ .fromStatsList(dfsBlockCacheTable.getBlockCacheStats());
+ }
+
static final class Ref<T> {
final DfsStreamKey key;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
index 77273cec61..17bf51876d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
@@ -11,17 +11,27 @@
package org.eclipse.jgit.internal.storage.dfs;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_CACHE_PREFIX;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_LIMIT;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_SIZE;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONCURRENCY_LEVEL;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACK_EXTENSIONS;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_RATIO;
+import java.io.PrintWriter;
import java.text.MessageFormat;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackExt;
@@ -41,25 +51,103 @@ public class DfsBlockCacheConfig {
/** Default number of max cache hits. */
public static final int DEFAULT_CACHE_HOT_MAX = 1;
+ static final String DEFAULT_NAME = "<default>"; //$NON-NLS-1$
+
+ private String name;
+
private long blockLimit;
+
private int blockSize;
+
private double streamRatio;
+
private int concurrencyLevel;
private Consumer<Long> refLock;
+
private Map<PackExt, Integer> cacheHotMap;
private IndexEventConsumer indexEventConsumer;
+ private List<DfsBlockCachePackExtConfig> packExtCacheConfigurations;
+
/**
* Create a default configuration.
*/
public DfsBlockCacheConfig() {
+ name = DEFAULT_NAME;
setBlockLimit(32 * MB);
setBlockSize(64 * KB);
setStreamRatio(0.30);
setConcurrencyLevel(32);
cacheHotMap = Collections.emptyMap();
+ packExtCacheConfigurations = Collections.emptyList();
+ }
+
+ /**
+ * Print the current cache configuration to the given {@link PrintWriter}.
+ *
+ * @param writer
+ * {@link PrintWriter} to write the cache's configuration to.
+ */
+ public void print(PrintWriter writer) {
+ print(/* linePrefix= */ "", /* pad= */ " ", writer); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ /**
+ * Print the current cache configuration to the given {@link PrintWriter}.
+ *
+ * @param linePrefix
+ * prefix to prepend all writen lines with. Ex a string of 0 or
+ * more " " entries.
+ * @param pad
+ * filler used to extend linePrefix. Ex a multiple of " ".
+ * @param writer
+ * {@link PrintWriter} to write the cache's configuration to.
+ */
+ @SuppressWarnings("nls")
+ private void print(String linePrefix, String pad, PrintWriter writer) {
+ String currentPrefixLevel = linePrefix;
+ if (!name.isEmpty() || !packExtCacheConfigurations.isEmpty()) {
+ writer.println(linePrefix + "Name: "
+ + (name.isEmpty() ? DEFAULT_NAME : this.name));
+ currentPrefixLevel += pad;
+ }
+ writer.println(currentPrefixLevel + "BlockLimit: " + blockLimit);
+ writer.println(currentPrefixLevel + "BlockSize: " + blockSize);
+ writer.println(currentPrefixLevel + "StreamRatio: " + streamRatio);
+ writer.println(
+ currentPrefixLevel + "ConcurrencyLevel: " + concurrencyLevel);
+ for (Map.Entry<PackExt, Integer> entry : cacheHotMap.entrySet()) {
+ writer.println(currentPrefixLevel + "CacheHotMapEntry: "
+ + entry.getKey() + " : " + entry.getValue());
+ }
+ for (DfsBlockCachePackExtConfig extConfig : packExtCacheConfigurations) {
+ extConfig.print(currentPrefixLevel, pad, writer);
+ }
+ }
+
+ /**
+ * Get the name for the block cache configured by this cache config.
+ *
+ * @return the name for the block cache configured by this cache config.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name for the block cache configured by this cache config.
+ * <p>
+ * Made visible for testing.
+ *
+ * @param name
+ * the name for the block cache configured by this cache config.
+ * @return {@code this}
+ */
+ DfsBlockCacheConfig setName(String name) {
+ this.name = name;
+ return this;
}
/**
@@ -77,10 +165,10 @@ public class DfsBlockCacheConfig {
* Set maximum number bytes of heap memory to dedicate to caching pack file
* data.
* <p>
- * It is strongly recommended to set the block limit to be an integer multiple
- * of the block size. This constraint is not enforced by this method (since
- * it may be called before {@link #setBlockSize(int)}), but it is enforced by
- * {@link #fromConfig(Config)}.
+ * It is strongly recommended to set the block limit to be an integer
+ * multiple of the block size. This constraint is not enforced by this
+ * method (since it may be called before {@link #setBlockSize(int)}), but it
+ * is enforced by {@link #fromConfig(Config)}.
*
* @param newLimit
* maximum number bytes of heap memory to dedicate to caching
@@ -89,9 +177,9 @@ public class DfsBlockCacheConfig {
*/
public DfsBlockCacheConfig setBlockLimit(long newLimit) {
if (newLimit <= 0) {
- throw new IllegalArgumentException(MessageFormat.format(
- JGitText.get().blockLimitNotPositive,
- Long.valueOf(newLimit)));
+ throw new IllegalArgumentException(
+ MessageFormat.format(JGitText.get().blockLimitNotPositive,
+ Long.valueOf(newLimit)));
}
blockLimit = newLimit;
return this;
@@ -211,12 +299,24 @@ public class DfsBlockCacheConfig {
* map of hot count per pack extension for {@code DfsBlockCache}.
* @return {@code this}
*/
+ /*
+ * TODO The cache HotMap configuration should be set as a config option and
+ * not passed in through a setter.
+ */
public DfsBlockCacheConfig setCacheHotMap(
Map<PackExt, Integer> cacheHotMap) {
this.cacheHotMap = Collections.unmodifiableMap(cacheHotMap);
+ setCacheHotMapToPackExtConfigs(this.cacheHotMap);
return this;
}
+ private void setCacheHotMapToPackExtConfigs(
+ Map<PackExt, Integer> cacheHotMap) {
+ for (DfsBlockCachePackExtConfig packExtConfig : packExtCacheConfigurations) {
+ packExtConfig.setCacheHotMap(cacheHotMap);
+ }
+ }
+
/**
* Get the consumer of cache index events.
*
@@ -240,61 +340,121 @@ public class DfsBlockCacheConfig {
}
/**
+ * Get the list of pack ext cache configs.
+ *
+ * @return the list of pack ext cache configs.
+ */
+ List<DfsBlockCachePackExtConfig> getPackExtCacheConfigurations() {
+ return packExtCacheConfigurations;
+ }
+
+ /**
+ * Set the list of pack ext cache configs.
+ *
+ * Made visible for testing.
+ *
+ * @param packExtCacheConfigurations
+ * the list of pack ext cache configs to set.
+ * @return {@code this}
+ */
+ DfsBlockCacheConfig setPackExtCacheConfigurations(
+ List<DfsBlockCachePackExtConfig> packExtCacheConfigurations) {
+ this.packExtCacheConfigurations = packExtCacheConfigurations;
+ return this;
+ }
+
+ /**
* Update properties by setting fields from the configuration.
* <p>
* If a property is not defined in the configuration, then it is left
* unmodified.
* <p>
- * Enforces certain constraints on the combination of settings in the config,
- * for example that the block limit is a multiple of the block size.
+ * Enforces certain constraints on the combination of settings in the
+ * config, for example that the block limit is a multiple of the block size.
*
* @param rc
* configuration to read properties from.
* @return {@code this}
*/
public DfsBlockCacheConfig fromConfig(Config rc) {
- long cfgBlockLimit = rc.getLong(
- CONFIG_CORE_SECTION,
- CONFIG_DFS_SECTION,
- CONFIG_KEY_BLOCK_LIMIT,
- getBlockLimit());
- int cfgBlockSize = rc.getInt(
- CONFIG_CORE_SECTION,
- CONFIG_DFS_SECTION,
- CONFIG_KEY_BLOCK_SIZE,
+ fromConfig(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION, rc);
+ loadPackExtConfigs(rc);
+ return this;
+ }
+
+ private void fromConfig(String section, String subSection, Config rc) {
+ long cfgBlockLimit = rc.getLong(section, subSection,
+ CONFIG_KEY_BLOCK_LIMIT, getBlockLimit());
+ int cfgBlockSize = rc.getInt(section, subSection, CONFIG_KEY_BLOCK_SIZE,
getBlockSize());
if (cfgBlockLimit % cfgBlockSize != 0) {
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().blockLimitNotMultipleOfBlockSize,
- Long.valueOf(cfgBlockLimit),
- Long.valueOf(cfgBlockSize)));
+ Long.valueOf(cfgBlockLimit), Long.valueOf(cfgBlockSize)));
}
+ // Set name only if `core dfs` is configured, otherwise fall back to the
+ // default.
+ if (rc.getSubsections(section).contains(subSection)) {
+ this.name = subSection;
+ }
setBlockLimit(cfgBlockLimit);
setBlockSize(cfgBlockSize);
- setConcurrencyLevel(rc.getInt(
- CONFIG_CORE_SECTION,
- CONFIG_DFS_SECTION,
- CONFIG_KEY_CONCURRENCY_LEVEL,
- getConcurrencyLevel()));
+ setConcurrencyLevel(rc.getInt(section, subSection,
+ CONFIG_KEY_CONCURRENCY_LEVEL, getConcurrencyLevel()));
- String v = rc.getString(
- CONFIG_CORE_SECTION,
- CONFIG_DFS_SECTION,
- CONFIG_KEY_STREAM_RATIO);
+ String v = rc.getString(section, subSection, CONFIG_KEY_STREAM_RATIO);
if (v != null) {
try {
setStreamRatio(Double.parseDouble(v));
} catch (NumberFormatException e) {
throw new IllegalArgumentException(MessageFormat.format(
- JGitText.get().enumValueNotSupported3,
- CONFIG_CORE_SECTION,
- CONFIG_DFS_SECTION,
- CONFIG_KEY_STREAM_RATIO, v), e);
+ JGitText.get().enumValueNotSupported3, section,
+ subSection, CONFIG_KEY_STREAM_RATIO, v), e);
}
}
- return this;
+ }
+
+ private void loadPackExtConfigs(Config config) {
+ List<String> subSections = config.getSubsections(CONFIG_CORE_SECTION)
+ .stream()
+ .filter(section -> section.startsWith(CONFIG_DFS_CACHE_PREFIX))
+ .collect(Collectors.toList());
+ if (subSections.size() == 0) {
+ return;
+ }
+ ArrayList<DfsBlockCachePackExtConfig> cacheConfigs = new ArrayList<>();
+ Set<PackExt> extensionsSeen = new HashSet<>();
+ for (String subSection : subSections) {
+ var cacheConfig = DfsBlockCachePackExtConfig.fromConfig(config,
+ CONFIG_CORE_SECTION, subSection);
+ Set<PackExt> packExtsDuplicates = intersection(extensionsSeen,
+ cacheConfig.packExts);
+ if (packExtsDuplicates.size() > 0) {
+ String duplicatePackExts = packExtsDuplicates.stream()
+ .map(PackExt::toString)
+ .collect(Collectors.joining(",")); //$NON-NLS-1$
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().duplicatePackExtensionsSet,
+ CONFIG_CORE_SECTION, subSection,
+ CONFIG_KEY_PACK_EXTENSIONS, duplicatePackExts));
+ }
+ extensionsSeen.addAll(cacheConfig.packExts);
+ cacheConfigs.add(cacheConfig);
+ }
+ packExtCacheConfigurations = cacheConfigs;
+ setCacheHotMapToPackExtConfigs(this.cacheHotMap);
+ }
+
+ private static <T> Set<T> intersection(Set<T> first, Set<T> second) {
+ Set<T> ret = new HashSet<>();
+ for (T entry : second) {
+ if (first.contains(entry)) {
+ ret.add(entry);
+ }
+ }
+ return ret;
}
/** Consumer of DfsBlockCache loading and eviction events for indexes. */
@@ -346,4 +506,102 @@ public class DfsBlockCacheConfig {
return false;
}
}
-} \ No newline at end of file
+
+ /**
+ * A configuration for a single cache table storing 1 or more Pack
+ * extensions.
+ * <p>
+ * The current pack ext cache tables implementation supports the same
+ * parameters the ClockBlockCacheTable (current default implementation).
+ * <p>
+ * Configuration falls back to the defaults coded values defined in the
+ * {@link DfsBlockCacheConfig} when not set on each cache table
+ * configuration and NOT the values of the basic dfs section.
+ * <p>
+ * <code>
+ *
+ * Format:
+ * [core "dfs.packCache"]
+ * packExtensions = "PACK"
+ * blockSize = 512
+ * blockLimit = 100
+ * concurrencyLevel = 5
+ *
+ * [core "dfs.multipleExtensionCache"]
+ * packExtensions = "INDEX REFTABLE BITMAP_INDEX"
+ * blockSize = 512
+ * blockLimit = 100
+ * concurrencyLevel = 5
+ * </code>
+ */
+ static class DfsBlockCachePackExtConfig {
+ // Set of pack extensions that will map to the cache instance.
+ private final EnumSet<PackExt> packExts;
+
+ // Configuration for the cache instance.
+ private final DfsBlockCacheConfig packExtCacheConfiguration;
+
+ /**
+ * Made visible for testing.
+ *
+ * @param packExts
+ * Set of {@link PackExt}s associated to this cache config.
+ * @param packExtCacheConfiguration
+ * {@link DfsBlockCacheConfig} for this cache config.
+ */
+ DfsBlockCachePackExtConfig(EnumSet<PackExt> packExts,
+ DfsBlockCacheConfig packExtCacheConfiguration) {
+ this.packExts = packExts;
+ this.packExtCacheConfiguration = packExtCacheConfiguration;
+ }
+
+ Set<PackExt> getPackExts() {
+ return packExts;
+ }
+
+ DfsBlockCacheConfig getPackExtCacheConfiguration() {
+ return packExtCacheConfiguration;
+ }
+
+ void setCacheHotMap(Map<PackExt, Integer> cacheHotMap) {
+ Map<PackExt, Integer> packExtHotMap = packExts.stream()
+ .filter(cacheHotMap::containsKey)
+ .collect(Collectors.toUnmodifiableMap(Function.identity(),
+ cacheHotMap::get));
+ packExtCacheConfiguration.setCacheHotMap(packExtHotMap);
+ }
+
+ private static DfsBlockCachePackExtConfig fromConfig(Config config,
+ String section, String subSection) {
+ String packExtensions = config.getString(section, subSection,
+ CONFIG_KEY_PACK_EXTENSIONS);
+ if (packExtensions == null) {
+ throw new IllegalArgumentException(
+ JGitText.get().noPackExtGivenForConfiguration);
+ }
+ String[] extensions = packExtensions.split(" ", -1); //$NON-NLS-1$
+ Set<PackExt> packExts = new HashSet<>(extensions.length);
+ for (String extension : extensions) {
+ try {
+ packExts.add(PackExt.valueOf(extension));
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().unknownPackExtension, section,
+ subSection, CONFIG_KEY_PACK_EXTENSIONS, extension),
+ e);
+ }
+ }
+
+ DfsBlockCacheConfig dfsBlockCacheConfig = new DfsBlockCacheConfig();
+ dfsBlockCacheConfig.fromConfig(section, subSection, config);
+ return new DfsBlockCachePackExtConfig(EnumSet.copyOf(packExts),
+ dfsBlockCacheConfig);
+ }
+
+ void print(String linePrefix, String pad, PrintWriter writer) {
+ packExtCacheConfiguration.print(linePrefix, pad, writer);
+ writer.println(linePrefix + pad + "PackExts: " //$NON-NLS-1$
+ + packExts.stream().sorted().collect(Collectors.toList()));
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheStats.java
new file mode 100644
index 0000000000..436f57437e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheStats.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2024, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheTable.BlockCacheStats;
+
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+
+/**
+ * Keeps track of stats for a Block Cache table.
+ */
+class DfsBlockCacheStats implements BlockCacheStats {
+ private final String name;
+
+ /**
+ * Number of times a block was found in the cache, per pack file extension.
+ */
+ private final AtomicReference<AtomicLong[]> statHit;
+
+ /**
+ * Number of times a block was not found, and had to be loaded, per pack
+ * file extension.
+ */
+ private final AtomicReference<AtomicLong[]> statMiss;
+
+ /**
+ * Number of blocks evicted due to cache being full, per pack file
+ * extension.
+ */
+ private final AtomicReference<AtomicLong[]> statEvict;
+
+ /**
+ * Number of bytes currently loaded in the cache, per pack file extension.
+ */
+ private final AtomicReference<AtomicLong[]> liveBytes;
+
+ DfsBlockCacheStats() {
+ this(""); //$NON-NLS-1$
+ }
+
+ DfsBlockCacheStats(String name) {
+ this.name = name;
+ statHit = new AtomicReference<>(newCounters());
+ statMiss = new AtomicReference<>(newCounters());
+ statEvict = new AtomicReference<>(newCounters());
+ liveBytes = new AtomicReference<>(newCounters());
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Increment the {@code statHit} count.
+ *
+ * @param key
+ * key identifying which liveBytes entry to update.
+ */
+ void incrementHit(DfsStreamKey key) {
+ getStat(statHit, key).incrementAndGet();
+ }
+
+ /**
+ * Increment the {@code statMiss} count.
+ *
+ * @param key
+ * key identifying which liveBytes entry to update.
+ */
+ void incrementMiss(DfsStreamKey key) {
+ getStat(statMiss, key).incrementAndGet();
+ }
+
+ /**
+ * Increment the {@code statEvict} count.
+ *
+ * @param key
+ * key identifying which liveBytes entry to update.
+ */
+ void incrementEvict(DfsStreamKey key) {
+ getStat(statEvict, key).incrementAndGet();
+ }
+
+ /**
+ * Add {@code size} to the {@code liveBytes} count.
+ *
+ * @param key
+ * key identifying which liveBytes entry to update.
+ * @param size
+ * amount to increment the count by.
+ */
+ void addToLiveBytes(DfsStreamKey key, long size) {
+ getStat(liveBytes, key).addAndGet(size);
+ }
+
+ @Override
+ public long[] getCurrentSize() {
+ return getStatVals(liveBytes);
+ }
+
+ @Override
+ public long[] getHitCount() {
+ return getStatVals(statHit);
+ }
+
+ @Override
+ public long[] getMissCount() {
+ return getStatVals(statMiss);
+ }
+
+ @Override
+ public long[] getTotalRequestCount() {
+ AtomicLong[] hit = statHit.get();
+ AtomicLong[] miss = statMiss.get();
+ long[] cnt = new long[Math.max(hit.length, miss.length)];
+ for (int i = 0; i < hit.length; i++) {
+ cnt[i] += hit[i].get();
+ }
+ for (int i = 0; i < miss.length; i++) {
+ cnt[i] += miss[i].get();
+ }
+ return cnt;
+ }
+
+ @Override
+ public long[] getHitRatio() {
+ AtomicLong[] hit = statHit.get();
+ AtomicLong[] miss = statMiss.get();
+ long[] ratio = new long[Math.max(hit.length, miss.length)];
+ for (int i = 0; i < ratio.length; i++) {
+ if (i >= hit.length) {
+ ratio[i] = 0;
+ } else if (i >= miss.length) {
+ ratio[i] = 100;
+ } else {
+ long hitVal = hit[i].get();
+ long missVal = miss[i].get();
+ long total = hitVal + missVal;
+ ratio[i] = total == 0 ? 0 : hitVal * 100 / total;
+ }
+ }
+ return ratio;
+ }
+
+ @Override
+ public long[] getEvictions() {
+ return getStatVals(statEvict);
+ }
+
+ private static AtomicLong[] newCounters() {
+ AtomicLong[] ret = new AtomicLong[PackExt.values().length];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = new AtomicLong();
+ }
+ return ret;
+ }
+
+ private static long[] getStatVals(AtomicReference<AtomicLong[]> stat) {
+ AtomicLong[] stats = stat.get();
+ long[] cnt = new long[stats.length];
+ for (int i = 0; i < stats.length; i++) {
+ cnt[i] = stats[i].get();
+ }
+ return cnt;
+ }
+
+ private static AtomicLong getStat(AtomicReference<AtomicLong[]> stats,
+ DfsStreamKey key) {
+ int pos = key.packExtPos;
+ while (true) {
+ AtomicLong[] vals = stats.get();
+ if (pos < vals.length) {
+ return vals[pos];
+ }
+ AtomicLong[] expect = vals;
+ vals = new AtomicLong[Math.max(pos + 1, PackExt.values().length)];
+ System.arraycopy(expect, 0, vals, 0, expect.length);
+ for (int i = expect.length; i < vals.length; i++) {
+ vals[i] = new AtomicLong();
+ }
+ if (stats.compareAndSet(expect, vals)) {
+ return vals[pos];
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTable.java
index 701d1fdce3..c3fd07b7bb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTable.java
@@ -11,10 +11,7 @@
package org.eclipse.jgit.internal.storage.dfs;
import java.io.IOException;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jgit.internal.storage.pack.PackExt;
+import java.util.List;
/**
* Block cache table.
@@ -129,99 +126,43 @@ public interface DfsBlockCacheTable {
<T> T get(DfsStreamKey key, long position);
/**
- * Get the DfsBlockCacheStats object for this block cache table's
- * statistics.
+ * Get the list of {@link BlockCacheStats} held by this cache.
+ * <p>
+ * The returned list has a {@link BlockCacheStats} per configured cache
+ * table, with a minimum of 1 {@link BlockCacheStats} object returned.
+ *
+ * Use {@link AggregatedBlockCacheStats} to combine the results of the stats
+ * in the list for an aggregated view of the cache's stats.
*
- * @return the DfsBlockCacheStats tracking this block cache table's
- * statistics.
+ * @return the list of {@link BlockCacheStats} held by this cache.
*/
- DfsBlockCacheStats getDfsBlockCacheStats();
+ List<BlockCacheStats> getBlockCacheStats();
/**
- * Keeps track of stats for a Block Cache table.
+ * Get the name of the table.
+ *
+ * @return this table's name.
*/
- class DfsBlockCacheStats {
- /**
- * Number of times a block was found in the cache, per pack file
- * extension.
- */
- private final AtomicReference<AtomicLong[]> statHit;
-
- /**
- * Number of times a block was not found, and had to be loaded, per pack
- * file extension.
- */
- private final AtomicReference<AtomicLong[]> statMiss;
-
- /**
- * Number of blocks evicted due to cache being full, per pack file
- * extension.
- */
- private final AtomicReference<AtomicLong[]> statEvict;
-
- /**
- * Number of bytes currently loaded in the cache, per pack file
- * extension.
- */
- private final AtomicReference<AtomicLong[]> liveBytes;
-
- DfsBlockCacheStats() {
- statHit = new AtomicReference<>(newCounters());
- statMiss = new AtomicReference<>(newCounters());
- statEvict = new AtomicReference<>(newCounters());
- liveBytes = new AtomicReference<>(newCounters());
- }
-
- /**
- * Increment the {@code statHit} count.
- *
- * @param key
- * key identifying which liveBytes entry to update.
- */
- void incrementHit(DfsStreamKey key) {
- getStat(statHit, key).incrementAndGet();
- }
-
- /**
- * Increment the {@code statMiss} count.
- *
- * @param key
- * key identifying which liveBytes entry to update.
- */
- void incrementMiss(DfsStreamKey key) {
- getStat(statMiss, key).incrementAndGet();
- }
+ String getName();
- /**
- * Increment the {@code statEvict} count.
- *
- * @param key
- * key identifying which liveBytes entry to update.
- */
- void incrementEvict(DfsStreamKey key) {
- getStat(statEvict, key).incrementAndGet();
- }
+ /**
+ * Provides methods used with Block Cache statistics.
+ */
+ interface BlockCacheStats {
/**
- * Add {@code size} to the {@code liveBytes} count.
+ * Get the name of the block cache generating this instance.
*
- * @param key
- * key identifying which liveBytes entry to update.
- * @param size
- * amount to increment the count by.
+ * @return this cache's name.
*/
- void addToLiveBytes(DfsStreamKey key, long size) {
- getStat(liveBytes, key).addAndGet(size);
- }
+ String getName();
/**
* Get total number of bytes in the cache, per pack file extension.
*
* @return total number of bytes in the cache, per pack file extension.
*/
- long[] getCurrentSize() {
- return getStatVals(liveBytes);
- }
+ long[] getCurrentSize();
/**
* Get number of requests for items in the cache, per pack file
@@ -230,9 +171,7 @@ public interface DfsBlockCacheTable {
* @return the number of requests for items in the cache, per pack file
* extension.
*/
- long[] getHitCount() {
- return getStatVals(statHit);
- }
+ long[] getHitCount();
/**
* Get number of requests for items not in the cache, per pack file
@@ -241,9 +180,7 @@ public interface DfsBlockCacheTable {
* @return the number of requests for items not in the cache, per pack
* file extension.
*/
- long[] getMissCount() {
- return getStatVals(statMiss);
- }
+ long[] getMissCount();
/**
* Get total number of requests (hit + miss), per pack file extension.
@@ -251,42 +188,14 @@ public interface DfsBlockCacheTable {
* @return total number of requests (hit + miss), per pack file
* extension.
*/
- long[] getTotalRequestCount() {
- AtomicLong[] hit = statHit.get();
- AtomicLong[] miss = statMiss.get();
- long[] cnt = new long[Math.max(hit.length, miss.length)];
- for (int i = 0; i < hit.length; i++) {
- cnt[i] += hit[i].get();
- }
- for (int i = 0; i < miss.length; i++) {
- cnt[i] += miss[i].get();
- }
- return cnt;
- }
+ long[] getTotalRequestCount();
/**
* Get hit ratios.
*
* @return hit ratios.
*/
- long[] getHitRatio() {
- AtomicLong[] hit = statHit.get();
- AtomicLong[] miss = statMiss.get();
- long[] ratio = new long[Math.max(hit.length, miss.length)];
- for (int i = 0; i < ratio.length; i++) {
- if (i >= hit.length) {
- ratio[i] = 0;
- } else if (i >= miss.length) {
- ratio[i] = 100;
- } else {
- long hitVal = hit[i].get();
- long missVal = miss[i].get();
- long total = hitVal + missVal;
- ratio[i] = total == 0 ? 0 : hitVal * 100 / total;
- }
- }
- return ratio;
- }
+ long[] getHitRatio();
/**
* Get number of evictions performed due to cache being full, per pack
@@ -295,46 +204,6 @@ public interface DfsBlockCacheTable {
* @return the number of evictions performed due to cache being full,
* per pack file extension.
*/
- long[] getEvictions() {
- return getStatVals(statEvict);
- }
-
- private static AtomicLong[] newCounters() {
- AtomicLong[] ret = new AtomicLong[PackExt.values().length];
- for (int i = 0; i < ret.length; i++) {
- ret[i] = new AtomicLong();
- }
- return ret;
- }
-
- private static long[] getStatVals(AtomicReference<AtomicLong[]> stat) {
- AtomicLong[] stats = stat.get();
- long[] cnt = new long[stats.length];
- for (int i = 0; i < stats.length; i++) {
- cnt[i] = stats[i].get();
- }
- return cnt;
- }
-
- private static AtomicLong getStat(AtomicReference<AtomicLong[]> stats,
- DfsStreamKey key) {
- int pos = key.packExtPos;
- while (true) {
- AtomicLong[] vals = stats.get();
- if (pos < vals.length) {
- return vals[pos];
- }
- AtomicLong[] expect = vals;
- vals = new AtomicLong[Math.max(pos + 1,
- PackExt.values().length)];
- System.arraycopy(expect, 0, vals, 0, expect.length);
- for (int i = expect.length; i < vals.length; i++) {
- vals[i] = new AtomicLong();
- }
- if (stats.compareAndSet(expect, vals)) {
- return vals[pos];
- }
- }
- }
+ long[] getEvictions();
}
-} \ No newline at end of file
+}
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 a177669788..199481cf33 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
@@ -18,13 +18,13 @@ import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.RE
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
import static org.eclipse.jgit.internal.storage.dfs.DfsPackCompactor.configureReftable;
import static org.eclipse.jgit.internal.storage.pack.PackExt.COMMIT_GRAPH;
-import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.OBJECT_SIZE_INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
import static org.eclipse.jgit.internal.storage.pack.PackWriter.NONE;
import java.io.IOException;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -90,7 +90,7 @@ public class DfsGarbageCollector {
private long coalesceGarbageLimit = 50 << 20;
private long garbageTtlMillis = TimeUnit.DAYS.toMillis(1);
- private long startTimeMillis;
+ private Instant startTime;
private List<DfsPackFile> packsBefore;
private List<DfsReftable> reftablesBefore;
private List<DfsPackFile> expiredGarbagePacks;
@@ -100,6 +100,7 @@ public class DfsGarbageCollector {
private Set<ObjectId> allTags;
private Set<ObjectId> nonHeads;
private Set<ObjectId> tagTargets;
+ private Instant refLogExpire;
/**
* Initialize a garbage collector.
@@ -200,6 +201,22 @@ public class DfsGarbageCollector {
return this;
}
+
+ /**
+ * Set time limit to the reflog history.
+ * <p>
+ * Garbage Collector prunes entries from reflog history older than {@code refLogExpire}
+ * <p>
+ *
+ * @param refLogExpire
+ * instant in time which defines refLog expiration
+ * @return {@code this}
+ */
+ public DfsGarbageCollector setRefLogExpire(Instant refLogExpire) {
+ this.refLogExpire = refLogExpire;
+ return this;
+ }
+
/**
* Set maxUpdateIndex for the initial reftable created during conversion.
*
@@ -335,7 +352,7 @@ public class DfsGarbageCollector {
throw new IllegalStateException(
JGitText.get().supportOnlyPackIndexVersion2);
- startTimeMillis = SystemReader.getInstance().getCurrentTime();
+ startTime = SystemReader.getInstance().now();
ctx = objdb.newReader();
try {
refdb.refresh();
@@ -418,7 +435,7 @@ public class DfsGarbageCollector {
packsBefore = new ArrayList<>(packs.length);
expiredGarbagePacks = new ArrayList<>(packs.length);
- long now = SystemReader.getInstance().getCurrentTime();
+ long now = SystemReader.getInstance().now().toEpochMilli();
for (DfsPackFile p : packs) {
DfsPackDescription d = p.getPackDescription();
if (d.getPackSource() != UNREACHABLE_GARBAGE) {
@@ -687,14 +704,7 @@ public class DfsGarbageCollector {
pack.setBlockSize(PACK, out.blockSize());
}
- try (DfsOutputStream out = objdb.writeFile(pack, INDEX)) {
- CountingOutputStream cnt = new CountingOutputStream(out);
- pw.writeIndex(cnt);
- pack.addFileExt(INDEX);
- pack.setFileSize(INDEX, cnt.getCount());
- pack.setBlockSize(INDEX, out.blockSize());
- pack.setIndexVersion(pw.getIndexVersion());
- }
+ pw.writeIndex(objdb.getPackIndexWriter(pack, pw.getIndexVersion()));
if (source != UNREACHABLE_GARBAGE && packConfig.getMinBytesForObjSizeIndex() >= 0) {
try (DfsOutputStream out = objdb.writeFile(pack,
@@ -713,7 +723,7 @@ public class DfsGarbageCollector {
PackStatistics stats = pw.getStatistics();
pack.setPackStats(stats);
- pack.setLastModified(startTimeMillis);
+ pack.setLastModified(startTime.toEpochMilli());
newPackDesc.add(pack);
newPackStats.add(stats);
newPackObj.add(pw.getObjectSet());
@@ -741,6 +751,10 @@ public class DfsGarbageCollector {
compact.addAll(stack.readers());
compact.setIncludeDeletes(includeDeletes);
compact.setConfig(configureReftable(reftableConfig, out));
+ if(refLogExpire != null ){
+ compact.setReflogExpireOldestReflogTimeMillis(
+ refLogExpire.toEpochMilli());
+ }
compact.compact();
pack.addFileExt(REFTABLE);
pack.setReftableStats(compact.getStats());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
index a07d8416c4..16315bf4f2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
@@ -41,12 +41,13 @@ import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.BasePackIndexWriter;
import org.eclipse.jgit.internal.storage.file.PackIndex;
-import org.eclipse.jgit.internal.storage.file.PackIndexWriter;
import org.eclipse.jgit.internal.storage.file.PackObjectSizeIndexWriter;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdOwnerMap;
@@ -54,7 +55,6 @@ import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ObjectStream;
-import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.transport.PackedObjectInfo;
import org.eclipse.jgit.util.BlockList;
import org.eclipse.jgit.util.IO;
@@ -71,6 +71,8 @@ public class DfsInserter extends ObjectInserter {
private static final int INDEX_VERSION = 2;
final DfsObjDatabase db;
+
+ private final int minBytesForObjectSizeIndex;
int compression = Deflater.BEST_COMPRESSION;
List<PackedObjectInfo> objectList;
@@ -83,8 +85,6 @@ public class DfsInserter extends ObjectInserter {
private boolean rollback;
private boolean checkExisting = true;
- private int minBytesForObjectSizeIndex = -1;
-
/**
* Initialize a new inserter.
*
@@ -93,8 +93,9 @@ public class DfsInserter extends ObjectInserter {
*/
protected DfsInserter(DfsObjDatabase db) {
this.db = db;
- PackConfig pc = new PackConfig(db.getRepository().getConfig());
- this.minBytesForObjectSizeIndex = pc.getMinBytesForObjSizeIndex();
+ this.minBytesForObjectSizeIndex = db.getRepository().getConfig().getInt(
+ ConfigConstants.CONFIG_PACK_SECTION,
+ ConfigConstants.CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX, -1);
}
/**
@@ -112,21 +113,6 @@ public class DfsInserter extends ObjectInserter {
void setCompressionLevel(int compression) {
this.compression = compression;
}
-
- /**
- * Set minimum size for an object to be included in the object size index.
- *
- * <p>
- * Use 0 for all and -1 for nothing (the pack won't have object size index).
- *
- * @param minBytes
- * only objects with size bigger or equal to this are included in
- * the index.
- */
- protected void setMinBytesForObjectSizeIndex(int minBytes) {
- this.minBytesForObjectSizeIndex = minBytes;
- }
-
@Override
public DfsPackParser newPackParser(InputStream in) throws IOException {
return new DfsPackParser(db, this, in);
@@ -333,7 +319,7 @@ public class DfsInserter extends ObjectInserter {
private static void index(OutputStream out, byte[] packHash,
List<PackedObjectInfo> list) throws IOException {
- PackIndexWriter.createVersion(out, INDEX_VERSION).write(list, packHash);
+ BasePackIndexWriter.createVersion(out, INDEX_VERSION).write(list, packHash);
}
void writeObjectSizeIndex(DfsPackDescription pack,
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 616563ffdd..efd666ff27 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
@@ -12,6 +12,7 @@ package org.eclipse.jgit.internal.storage.dfs;
import static java.util.stream.Collectors.joining;
import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -27,7 +28,9 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
+import org.eclipse.jgit.internal.storage.file.BasePackIndexWriter;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndexWriterV1;
+import org.eclipse.jgit.internal.storage.pack.PackIndexWriter;
import org.eclipse.jgit.internal.storage.pack.PackBitmapIndexWriter;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.lib.AnyObjectId;
@@ -771,4 +774,35 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
}
};
}
+
+ /**
+ * Returns a writer to store the pack index in this object database.
+ *
+ * @param pack
+ * Pack file to which the index is associated.
+ * @param indexVersion
+ * which version of the index to write
+ * @return a writer to store the index associated with the pack
+ * @throws IOException
+ * when some I/O problem occurs while creating or writing to
+ * output stream
+ */
+ public PackIndexWriter getPackIndexWriter(
+ DfsPackDescription pack, int indexVersion)
+ throws IOException {
+ return (objectsToStore, packDataChecksum) -> {
+ try (DfsOutputStream out = writeFile(pack, INDEX);
+ CountingOutputStream cnt = new CountingOutputStream(out)) {
+ final PackIndexWriter iw = BasePackIndexWriter
+ .createVersion(cnt,
+ indexVersion);
+ iw.write(objectsToStore, packDataChecksum);
+ pack.addFileExt(INDEX);
+ pack.setFileSize(INDEX, cnt.getCount());
+ pack.setBlockSize(INDEX, out.blockSize());
+ pack.setIndexVersion(indexVersion);
+ }
+ };
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
index 86144b389c..f9c01b9d6e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
@@ -12,7 +12,7 @@ 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.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.OBJECT_SIZE_INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
import static org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation.PACK_DELTA;
@@ -249,6 +249,7 @@ public class DfsPackCompactor {
try {
writePack(objdb, outDesc, pw, pm);
writeIndex(objdb, outDesc, pw);
+ writeObjectSizeIndex(objdb, outDesc, pw);
PackStatistics stats = pw.getStatistics();
@@ -458,13 +459,20 @@ public class DfsPackCompactor {
private static void writeIndex(DfsObjDatabase objdb,
DfsPackDescription pack,
PackWriter pw) throws IOException {
- try (DfsOutputStream out = objdb.writeFile(pack, INDEX)) {
+ pw.writeIndex(objdb.getPackIndexWriter(pack, pw.getIndexVersion()));
+ }
+
+ private static void writeObjectSizeIndex(DfsObjDatabase objdb,
+ DfsPackDescription pack,
+ PackWriter pw) throws IOException {
+ try (DfsOutputStream out = objdb.writeFile(pack, OBJECT_SIZE_INDEX)) {
CountingOutputStream cnt = new CountingOutputStream(out);
- pw.writeIndex(cnt);
- pack.addFileExt(INDEX);
- pack.setFileSize(INDEX, cnt.getCount());
- pack.setBlockSize(INDEX, out.blockSize());
- pack.setIndexVersion(pw.getIndexVersion());
+ pw.writeObjectSizeIndex(cnt);
+ if (cnt.getCount() > 0) {
+ pack.addFileExt(OBJECT_SIZE_INDEX);
+ pack.setFileSize(OBJECT_SIZE_INDEX, cnt.getCount());
+ pack.setBlockSize(OBJECT_SIZE_INDEX, out.blockSize());
+ }
}
}
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 5cc2a57aba..48ed47a77c 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
@@ -29,6 +29,7 @@ import java.nio.channels.Channels;
import java.text.MessageFormat;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.CRC32;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -106,6 +107,53 @@ public final class DfsPackFile extends BlockBasedFile {
/** Lock for {@link #corruptObjects}. */
private final Object corruptObjectsLock = new Object();
+ private final IndexFactory indexFactory;
+
+ /**
+ * Returns the indexes for this pack.
+ * <p>
+ * We define indexes in different sub interfaces to allow implementing the
+ * indexes over different combinations of backends.
+ * <p>
+ * Implementations decide if/how to cache the indexes. The calling
+ * DfsPackFile will keep the reference to the index as long as it needs it.
+ */
+ public interface IndexFactory {
+ /**
+ * Take care of loading the primary and reverse indexes for this pack.
+ */
+ interface PackIndexes {
+ /**
+ * Load the primary index for the pack.
+ *
+ * @param ctx
+ * reader to find the raw bytes
+ * @return a primary index
+ * @throws IOException
+ * a problem finding/parsing the index
+ */
+ PackIndex index(DfsReader ctx) throws IOException;
+
+ /**
+ * Load the reverse index of the pack
+ *
+ * @param ctx
+ * reader to find the raw bytes
+ * @return the reverse index of the pack
+ * @throws IOException
+ * a problem finding/parsing the reverse index
+ */
+ PackReverseIndex reverseIndex(DfsReader ctx) throws IOException;
+ }
+
+ /**
+ * Returns a provider of the primary and reverse indexes of this pack
+ *
+ * @return an implementation of the {@link PackIndexes} interface
+ */
+ PackIndexes getPackIndexes();
+ }
+
/**
* Construct a reader for an existing, packfile.
*
@@ -115,7 +163,8 @@ public final class DfsPackFile extends BlockBasedFile {
* description of the pack within the DFS.
*/
DfsPackFile(DfsBlockCache cache, DfsPackDescription desc) {
- this(cache, desc, DEFAULT_BITMAP_LOADER);
+ this(cache, desc, DEFAULT_BITMAP_LOADER,
+ new CachedStreamIndexFactory(cache, desc));
}
/**
@@ -127,9 +176,11 @@ public final class DfsPackFile extends BlockBasedFile {
* description of the pack within the DFS
* @param bitmapLoader
* loader to get the bitmaps of this pack (if any)
+ * @param indexFactory
+ * an IndexFactory to get references to the indexes of this pack
*/
public DfsPackFile(DfsBlockCache cache, DfsPackDescription desc,
- PackBitmapIndexLoader bitmapLoader) {
+ PackBitmapIndexLoader bitmapLoader, IndexFactory indexFactory) {
super(cache, desc, PACK);
int bs = desc.getBlockSize(PACK);
@@ -141,6 +192,7 @@ public final class DfsPackFile extends BlockBasedFile {
length = sz > 0 ? sz : -1;
this.bitmapLoader = bitmapLoader;
+ this.indexFactory = indexFactory;
}
/**
@@ -195,19 +247,10 @@ public final class DfsPackFile extends BlockBasedFile {
Repository.getGlobalListenerList()
.dispatch(new BeforeDfsPackIndexLoadedEvent(this));
try {
- DfsStreamKey idxKey = desc.getStreamKey(INDEX);
- AtomicBoolean cacheHit = new AtomicBoolean(true);
- DfsBlockCache.Ref<PackIndex> idxref = cache.getOrLoadRef(idxKey,
- REF_POSITION, () -> {
- cacheHit.set(false);
- return loadPackIndex(ctx, idxKey);
- });
- if (cacheHit.get()) {
- ctx.stats.idxCacheHit++;
- }
- PackIndex idx = idxref.get();
- if (index == null && idx != null) {
- index = idx;
+ index = indexFactory.getPackIndexes().index(ctx);
+ if (index == null) {
+ throw new IOException(
+ "Couldn't get a reference to the primary index"); //$NON-NLS-1$
}
ctx.emitIndexLoad(desc, INDEX, index);
return index;
@@ -321,20 +364,10 @@ public final class DfsPackFile extends BlockBasedFile {
return reverseIndex;
}
- PackIndex idx = idx(ctx);
- DfsStreamKey revKey = desc.getStreamKey(REVERSE_INDEX);
- AtomicBoolean cacheHit = new AtomicBoolean(true);
- DfsBlockCache.Ref<PackReverseIndex> revref = cache.getOrLoadRef(revKey,
- REF_POSITION, () -> {
- cacheHit.set(false);
- return loadReverseIdx(ctx, revKey, idx);
- });
- if (cacheHit.get()) {
- ctx.stats.ridxCacheHit++;
- }
- PackReverseIndex revidx = revref.get();
- if (reverseIndex == null && revidx != null) {
- reverseIndex = revidx;
+ reverseIndex = indexFactory.getPackIndexes().reverseIndex(ctx);
+ if (reverseIndex == null) {
+ throw new IOException(
+ "Couldn't get a reference to the reverse index"); //$NON-NLS-1$
}
ctx.emitIndexLoad(desc, REVERSE_INDEX, reverseIndex);
return reverseIndex;
@@ -347,6 +380,7 @@ public final class DfsPackFile extends BlockBasedFile {
}
if (objectSizeIndexLoadAttempted
+ || !ctx.getOptions().shouldUseObjectSizeIndex()
|| !desc.hasFileExt(OBJECT_SIZE_INDEX)) {
// Pack doesn't have object size index
return null;
@@ -1210,48 +1244,6 @@ public final class DfsPackFile extends BlockBasedFile {
}
}
- private DfsBlockCache.Ref<PackIndex> loadPackIndex(
- DfsReader ctx, DfsStreamKey idxKey) throws IOException {
- try {
- ctx.stats.readIdx++;
- long start = System.nanoTime();
- try (ReadableChannel rc = ctx.db.openFile(desc, INDEX)) {
- PackIndex idx = PackIndex.read(alignTo8kBlocks(rc));
- ctx.stats.readIdxBytes += rc.position();
- index = idx;
- return new DfsBlockCache.Ref<>(
- idxKey,
- REF_POSITION,
- idx.getObjectCount() * REC_SIZE,
- idx);
- } finally {
- ctx.stats.readIdxMicros += elapsedMicros(start);
- }
- } catch (EOFException e) {
- throw new IOException(MessageFormat.format(
- DfsText.get().shortReadOfIndex,
- desc.getFileName(INDEX)), e);
- } catch (IOException e) {
- throw new IOException(MessageFormat.format(
- DfsText.get().cannotReadIndex,
- desc.getFileName(INDEX)), e);
- }
- }
-
- private DfsBlockCache.Ref<PackReverseIndex> loadReverseIdx(
- DfsReader ctx, DfsStreamKey revKey, PackIndex idx) {
- ctx.stats.readReverseIdx++;
- long start = System.nanoTime();
- PackReverseIndex revidx = PackReverseIndexFactory.computeFromIndex(idx);
- reverseIndex = revidx;
- ctx.stats.readReverseIdxMicros += elapsedMicros(start);
- return new DfsBlockCache.Ref<>(
- revKey,
- REF_POSITION,
- idx.getObjectCount() * 8,
- revidx);
- }
-
private DfsBlockCache.Ref<PackObjectSizeIndex> loadObjectSizeIndex(
DfsReader ctx, DfsStreamKey objectSizeIndexKey) throws IOException {
ctx.stats.readObjectSizeIndex++;
@@ -1288,9 +1280,12 @@ public final class DfsPackFile extends BlockBasedFile {
private DfsBlockCache.Ref<PackBitmapIndex> loadBitmapIndex(DfsReader ctx,
DfsStreamKey bitmapKey) throws IOException {
ctx.stats.readBitmap++;
+ long start = System.nanoTime();
PackBitmapIndexLoader.LoadResult result = bitmapLoader
.loadPackBitmapIndex(ctx, this);
bitmapIndex = result.bitmapIndex;
+ ctx.stats.readBitmapIdxBytes += result.bytesRead;
+ ctx.stats.readBitmapIdxMicros += elapsedMicros(start);
return new DfsBlockCache.Ref<>(bitmapKey, REF_POSITION,
result.bytesRead, result.bitmapIndex);
}
@@ -1449,4 +1444,141 @@ public final class DfsPackFile extends BlockBasedFile {
}
}
}
+
+ /**
+ * An index factory backed by Dfs streams and references cached in
+ * DfsBlockCache
+ */
+ public static final class CachedStreamIndexFactory implements IndexFactory {
+ private final CachedStreamPackIndexes indexes;
+
+ /**
+ * An index factory
+ *
+ * @param cache
+ * DFS block cache to use for the references
+ * @param desc
+ * This factory loads indexes for this package
+ */
+ public CachedStreamIndexFactory(DfsBlockCache cache,
+ DfsPackDescription desc) {
+ this.indexes = new CachedStreamPackIndexes(cache, desc);
+ }
+
+ @Override
+ public PackIndexes getPackIndexes() {
+ return indexes;
+ }
+ }
+
+ /**
+ * Load primary and reverse index from Dfs streams and cache the references
+ * in DfsBlockCache.
+ */
+ public static final class CachedStreamPackIndexes implements IndexFactory.PackIndexes {
+ private final DfsBlockCache cache;
+
+ private final DfsPackDescription desc;
+
+ /**
+ * An index factory
+ *
+ * @param cache
+ * DFS block cache to use for the references
+ * @param desc This factory loads indexes for this package
+ */
+ public CachedStreamPackIndexes(DfsBlockCache cache,
+ DfsPackDescription desc) {
+ this.cache = cache;
+ this.desc = desc;
+ }
+
+ @Override
+ public PackIndex index(DfsReader ctx) throws IOException {
+ DfsStreamKey idxKey = desc.getStreamKey(INDEX);
+ // Keep the value parsed in the loader, in case the Ref<> is
+ // nullified in ClockBlockCacheTable#reserveSpace
+ // before we read its value.
+ AtomicReference<PackIndex> loadedRef = new AtomicReference<>(null);
+ DfsBlockCache.Ref<PackIndex> cachedRef = cache.getOrLoadRef(idxKey,
+ REF_POSITION, () -> {
+ RefWithSize<PackIndex> idx = loadPackIndex(ctx, desc);
+ loadedRef.set(idx.ref);
+ return new DfsBlockCache.Ref<>(idxKey, REF_POSITION,
+ idx.size, idx.ref);
+ });
+ if (loadedRef.get() == null) {
+ ctx.stats.idxCacheHit++;
+ }
+ return cachedRef.get() != null ? cachedRef.get() : loadedRef.get();
+ }
+
+ private static RefWithSize<PackIndex> loadPackIndex(DfsReader ctx,
+ DfsPackDescription desc) throws IOException {
+ try {
+ ctx.stats.readIdx++;
+ long start = System.nanoTime();
+ try (ReadableChannel rc = ctx.db.openFile(desc, INDEX)) {
+ PackIndex idx = PackIndex.read(alignTo8kBlocks(rc));
+ ctx.stats.readIdxBytes += rc.position();
+ return new RefWithSize<>(idx,
+ idx.getObjectCount() * REC_SIZE);
+ } finally {
+ ctx.stats.readIdxMicros += elapsedMicros(start);
+ }
+ } catch (EOFException e) {
+ throw new IOException(
+ MessageFormat.format(DfsText.get().shortReadOfIndex,
+ desc.getFileName(INDEX)),
+ e);
+ } catch (IOException e) {
+ throw new IOException(
+ MessageFormat.format(DfsText.get().cannotReadIndex,
+ desc.getFileName(INDEX)),
+ e);
+ }
+ }
+
+ @Override
+ public PackReverseIndex reverseIndex(DfsReader ctx) throws IOException {
+ PackIndex idx = index(ctx);
+ DfsStreamKey revKey = desc.getStreamKey(REVERSE_INDEX);
+ // Keep the value parsed in the loader, in case the Ref<> is
+ // nullified in ClockBlockCacheTable#reserveSpace
+ // before we read its value.
+ AtomicReference<PackReverseIndex> loadedRef = new AtomicReference<>(
+ null);
+ DfsBlockCache.Ref<PackReverseIndex> cachedRef = cache
+ .getOrLoadRef(revKey, REF_POSITION, () -> {
+ RefWithSize<PackReverseIndex> ridx = loadReverseIdx(ctx,
+ idx);
+ loadedRef.set(ridx.ref);
+ return new DfsBlockCache.Ref<>(revKey, REF_POSITION,
+ ridx.size, ridx.ref);
+ });
+ if (loadedRef.get() == null) {
+ ctx.stats.ridxCacheHit++;
+ }
+ return cachedRef.get() != null ? cachedRef.get() : loadedRef.get();
+ }
+
+ private static RefWithSize<PackReverseIndex> loadReverseIdx(
+ DfsReader ctx, PackIndex idx) {
+ ctx.stats.readReverseIdx++;
+ long start = System.nanoTime();
+ PackReverseIndex revidx = PackReverseIndexFactory
+ .computeFromIndex(idx);
+ ctx.stats.readReverseIdxMicros += elapsedMicros(start);
+ return new RefWithSize<>(revidx, idx.getObjectCount() * 8);
+ }
+ }
+
+ private static final class RefWithSize<V> {
+ final V ref;
+ final long size;
+ RefWithSize(V ref, long size) {
+ this.ref = ref;
+ this.size = size;
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
index c939114f5f..62f6753e5d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
@@ -307,7 +307,7 @@ public class DfsReader extends ObjectReader implements ObjectReuseAsIs {
private <T extends ObjectId> Iterable<FoundObject<T>> findAll(
Iterable<T> objectIds) throws IOException {
- Collection<T> pending = new ArrayList<>();
+ HashSet<T> pending = new HashSet<>();
for (T id : objectIds) {
pending.add(id);
}
@@ -327,22 +327,21 @@ public class DfsReader extends ObjectReader implements ObjectReuseAsIs {
}
private <T extends ObjectId> void findAllImpl(PackList packList,
- Collection<T> pending, List<FoundObject<T>> r) {
+ HashSet<T> pending, List<FoundObject<T>> r) {
DfsPackFile[] packs = packList.packs;
if (packs.length == 0) {
return;
}
int lastIdx = 0;
DfsPackFile lastPack = packs[lastIdx];
-
- OBJECT_SCAN: for (Iterator<T> it = pending.iterator(); it.hasNext();) {
- T t = it.next();
+ HashSet<T> toRemove = new HashSet<>();
+ OBJECT_SCAN: for (T t : pending) {
if (!skipGarbagePack(lastPack)) {
try {
long p = lastPack.findOffset(this, t);
if (0 < p) {
r.add(new FoundObject<>(t, lastIdx, lastPack, p));
- it.remove();
+ toRemove.add(t);
continue;
}
} catch (IOException e) {
@@ -360,7 +359,7 @@ public class DfsReader extends ObjectReader implements ObjectReuseAsIs {
long p = pack.findOffset(this, t);
if (0 < p) {
r.add(new FoundObject<>(t, i, pack, p));
- it.remove();
+ toRemove.add(t);
lastIdx = i;
lastPack = pack;
continue OBJECT_SCAN;
@@ -370,6 +369,7 @@ public class DfsReader extends ObjectReader implements ObjectReuseAsIs {
}
}
}
+ pending.removeAll(toRemove);
last = lastPack;
}
@@ -511,18 +511,15 @@ public class DfsReader extends ObjectReader implements ObjectReuseAsIs {
throw new MissingObjectException(objectId.copy(), typeHint);
}
- if (typeHint != Constants.OBJ_BLOB || !pack.hasObjectSizeIndex(this)) {
+ if (typeHint != Constants.OBJ_BLOB || !safeHasObjectSizeIndex(pack)) {
return pack.getObjectSize(this, objectId);
}
- long sz = pack.getIndexedObjectSize(this, objectId);
+ Optional<Long> maybeSz = safeGetIndexedObjectSize(pack, objectId);
+ long sz = maybeSz.orElse(-1L);
if (sz >= 0) {
- stats.objectSizeIndexHit += 1;
return sz;
}
-
- // Object wasn't in the index
- stats.objectSizeIndexMiss += 1;
return pack.getObjectSize(this, objectId);
}
@@ -541,23 +538,61 @@ public class DfsReader extends ObjectReader implements ObjectReuseAsIs {
}
stats.isNotLargerThanCallCount += 1;
- if (typeHint != Constants.OBJ_BLOB || !pack.hasObjectSizeIndex(this)) {
+ if (typeHint != Constants.OBJ_BLOB || !safeHasObjectSizeIndex(pack)) {
+ return pack.getObjectSize(this, objectId) <= limit;
+ }
+
+ Optional<Long> maybeSz = safeGetIndexedObjectSize(pack, objectId);
+ if (maybeSz.isEmpty()) {
+ // Exception in object size index
return pack.getObjectSize(this, objectId) <= limit;
}
- long sz = pack.getIndexedObjectSize(this, objectId);
+ long sz = maybeSz.get();
+ if (sz >= 0) {
+ return sz <= limit;
+ }
+
+ if (isLimitInsideIndexThreshold(pack, limit)) {
+ // With threshold T, not-found means object < T
+ // If limit L > T, then object < T < L
+ return true;
+ }
+
+ return pack.getObjectSize(this, objectId) <= limit;
+ }
+
+ private boolean safeHasObjectSizeIndex(DfsPackFile pack) {
+ try {
+ return pack.hasObjectSizeIndex(this);
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ private Optional<Long> safeGetIndexedObjectSize(DfsPackFile pack,
+ AnyObjectId objectId) {
+ long sz;
+ try {
+ sz = pack.getIndexedObjectSize(this, objectId);
+ } catch (IOException e) {
+ // Do not count the exception as an index miss
+ return Optional.empty();
+ }
if (sz < 0) {
stats.objectSizeIndexMiss += 1;
} else {
stats.objectSizeIndexHit += 1;
}
+ return Optional.of(sz);
+ }
- // Got size from index or we didn't but we are sure it should be there.
- if (sz >= 0 || pack.getObjectSizeIndexThreshold(this) <= limit) {
- return sz <= limit;
+ private boolean isLimitInsideIndexThreshold(DfsPackFile pack, long limit) {
+ try {
+ return pack.getObjectSizeIndexThreshold(this) <= limit;
+ } catch (IOException e) {
+ return false;
}
-
- return pack.getObjectSize(this, objectId) <= limit;
}
private DfsPackFile findPackWithObject(AnyObjectId objectId)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
index adb4673ae4..fcfa3e0dee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
@@ -40,12 +40,12 @@ public class DfsReaderIoStats {
/** Total number of complete pack indexes read into memory. */
long readIdx;
- /** Total number of complete bitmap indexes read into memory. */
- long readBitmap;
-
/** Total number of reverse indexes added into memory. */
long readReverseIdx;
+ /** Total number of complete bitmap indexes read into memory. */
+ long readBitmap;
+
/** Total number of complete commit graphs read into memory. */
long readCommitGraph;
@@ -55,6 +55,9 @@ public class DfsReaderIoStats {
/** Total number of bytes read from pack indexes. */
long readIdxBytes;
+ /** Total number of bytes read from bitmap indexes. */
+ long readBitmapIdxBytes;
+
/** Total number of bytes read from commit graphs. */
long readCommitGraphBytes;
@@ -67,18 +70,15 @@ public class DfsReaderIoStats {
/** Total microseconds spent creating reverse indexes. */
long readReverseIdxMicros;
+ /** Total microseconds spent reading bitmap indexes. */
+ long readBitmapIdxMicros;
+
/** Total microseconds spent creating commit graphs. */
long readCommitGraphMicros;
/** Total microseconds spent creating object size indexes */
long readObjectSizeIndexMicros;
- /** Total number of bytes read from bitmap indexes. */
- long readBitmapIdxBytes;
-
- /** Total microseconds spent reading bitmap indexes. */
- long readBitmapIdxMicros;
-
/** Total number of block cache hits. */
long blockCacheHit;
@@ -195,21 +195,21 @@ public class DfsReaderIoStats {
}
/**
- * Get total number of times the commit graph read into memory.
+ * Get total number of complete bitmap indexes read into memory.
*
- * @return total number of commit graph read into memory.
+ * @return total number of complete bitmap indexes read into memory.
*/
- public long getReadCommitGraphCount() {
- return stats.readCommitGraph;
+ public long getReadBitmapIndexCount() {
+ return stats.readBitmap;
}
/**
- * Get total number of complete bitmap indexes read into memory.
+ * Get total number of times the commit graph read into memory.
*
- * @return total number of complete bitmap indexes read into memory.
+ * @return total number of commit graph read into memory.
*/
- public long getReadBitmapIndexCount() {
- return stats.readBitmap;
+ public long getReadCommitGraphCount() {
+ return stats.readCommitGraph;
}
/**
@@ -231,6 +231,15 @@ public class DfsReaderIoStats {
}
/**
+ * Get total number of bytes read from bitmap indexes.
+ *
+ * @return total number of bytes read from bitmap indexes.
+ */
+ public long getReadBitmapIndexBytes() {
+ return stats.readBitmapIdxBytes;
+ }
+
+ /**
* Get total number of bytes read from commit graphs.
*
* @return total number of bytes read from commit graphs.
@@ -240,6 +249,15 @@ public class DfsReaderIoStats {
}
/**
+ * Get total number of bytes read from object size indexes.
+ *
+ * @return total number of bytes read from object size indexes.
+ */
+ public long getObjectSizeIndexBytes() {
+ return stats.readObjectSizeIndexBytes;
+ }
+
+ /**
* Get total microseconds spent reading pack indexes.
*
* @return total microseconds spent reading pack indexes.
@@ -258,30 +276,30 @@ public class DfsReaderIoStats {
}
/**
- * Get total microseconds spent reading commit graphs.
+ * Get total microseconds spent reading bitmap indexes.
*
- * @return total microseconds spent reading commit graphs.
+ * @return total microseconds spent reading bitmap indexes.
*/
- public long getReadCommitGraphMicros() {
- return stats.readCommitGraphMicros;
+ public long getReadBitmapIndexMicros() {
+ return stats.readBitmapIdxMicros;
}
/**
- * Get total number of bytes read from bitmap indexes.
+ * Get total microseconds spent reading commit graphs.
*
- * @return total number of bytes read from bitmap indexes.
+ * @return total microseconds spent reading commit graphs.
*/
- public long getReadBitmapIndexBytes() {
- return stats.readBitmapIdxBytes;
+ public long getReadCommitGraphMicros() {
+ return stats.readCommitGraphMicros;
}
/**
- * Get total microseconds spent reading bitmap indexes.
+ * Get total microseconds spent reading object size indexes.
*
- * @return total microseconds spent reading bitmap indexes.
+ * @return total microseconds spent reading object size indexes.
*/
- public long getReadBitmapIndexMicros() {
- return stats.readBitmapIdxMicros;
+ public long getReadObjectSizeIndexMicros() {
+ return stats.readObjectSizeIndexMicros;
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
index f2ac461deb..c3974691a1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
@@ -13,8 +13,10 @@ package org.eclipse.jgit.internal.storage.dfs;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_DELTA_BASE_CACHE_LIMIT;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_LOAD_REV_INDEX_IN_PARALLEL;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_BUFFER;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_FILE_THRESHOLD;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_USE_OBJECT_SIZE_INDEX;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.storage.pack.PackConfig;
@@ -36,6 +38,8 @@ public class DfsReaderOptions {
private boolean loadRevIndexInParallel;
+ private boolean useObjectSizeIndex;
+
/**
* Create a default reader configuration.
*/
@@ -137,6 +141,28 @@ public class DfsReaderOptions {
}
/**
+ * Use the object size index if available.
+ *
+ * @return true if the reader should try to use the object size index. if
+ * false, the reader ignores that index.
+ */
+ public boolean shouldUseObjectSizeIndex() {
+ return useObjectSizeIndex;
+ }
+
+ /**
+ * Set if the reader should try to use the object size index
+ *
+ * @param useObjectSizeIndex true to use it, false to ignore the object size index
+ *
+ * @return {@code this}
+ */
+ public DfsReaderOptions setUseObjectSizeIndex(boolean useObjectSizeIndex) {
+ this.useObjectSizeIndex = useObjectSizeIndex;
+ return this;
+ }
+
+ /**
* Update properties by setting fields from the configuration.
* <p>
* If a property is not defined in the configuration, then it is left
@@ -168,6 +194,13 @@ public class DfsReaderOptions {
CONFIG_DFS_SECTION,
CONFIG_KEY_STREAM_BUFFER,
getStreamPackBufferSize()));
+
+ setUseObjectSizeIndex(rc.getBoolean(CONFIG_CORE_SECTION,
+ CONFIG_DFS_SECTION, CONFIG_KEY_USE_OBJECT_SIZE_INDEX,
+ false));
+ setLoadRevIndexInParallel(
+ rc.getBoolean(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_LOAD_REV_INDEX_IN_PARALLEL, false));
return this;
}
}
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 3ba74b26fc..2751cd2969 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
@@ -28,6 +28,7 @@ import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.util.RefList;
@@ -177,6 +178,11 @@ public class DfsReftableDatabase extends DfsRefDatabase {
}
@Override
+ public ReflogReader getReflogReader(Ref ref) throws IOException {
+ return reftableDatabase.getReflogReader(ref.getName());
+ }
+
+ @Override
public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
if (!getReftableConfig().isIndexObjects()) {
return super.getTipsWithSha1(id);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTable.java
new file mode 100644
index 0000000000..bb44f9397a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTable.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2024, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.ReadableChannelSupplier;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.Ref;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.RefLoader;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DfsBlockCachePackExtConfig;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+
+/**
+ * A table that holds multiple cache tables accessed by {@link PackExt} types.
+ *
+ * <p>
+ * Allows the separation of entries from different {@link PackExt} types to
+ * limit churn in cache caused by entries of differing sizes.
+ * <p>
+ * Separating these tables enables the fine-tuning of cache tables per extension
+ * type.
+ */
+class PackExtBlockCacheTable implements DfsBlockCacheTable {
+ /**
+ * Table name.
+ */
+ private final String name;
+
+ private final DfsBlockCacheTable defaultBlockCacheTable;
+
+ // Holds the unique tables backing the extBlockCacheTables values.
+ private final List<DfsBlockCacheTable> blockCacheTableList;
+
+ // Holds the mapping of PackExt to DfsBlockCacheTables.
+ // The relation between the size of extBlockCacheTables entries and
+ // blockCacheTableList entries is:
+ // blockCacheTableList.size() <= extBlockCacheTables.size()
+ private final Map<PackExt, DfsBlockCacheTable> extBlockCacheTables;
+
+ /**
+ * Builds the PackExtBlockCacheTable from a list of
+ * {@link DfsBlockCachePackExtConfig}s.
+ *
+ * @param cacheConfig
+ * {@link DfsBlockCacheConfig} containing
+ * {@link DfsBlockCachePackExtConfig}s used to configure
+ * PackExtBlockCacheTable. The {@link DfsBlockCacheConfig} holds
+ * the configuration for the default cache table.
+ * @return the cache table built from the given configs.
+ * @throws IllegalArgumentException
+ * when no {@link DfsBlockCachePackExtConfig} exists in the
+ * {@link DfsBlockCacheConfig}.
+ */
+ static PackExtBlockCacheTable fromBlockCacheConfigs(
+ DfsBlockCacheConfig cacheConfig) {
+ DfsBlockCacheTable defaultTable = new ClockBlockCacheTable(cacheConfig);
+ Map<PackExt, DfsBlockCacheTable> packExtBlockCacheTables = new HashMap<>();
+ List<DfsBlockCachePackExtConfig> packExtConfigs = cacheConfig
+ .getPackExtCacheConfigurations();
+ if (packExtConfigs == null || packExtConfigs.size() == 0) {
+ throw new IllegalArgumentException(
+ JGitText.get().noPackExtConfigurationGiven);
+ }
+ for (DfsBlockCachePackExtConfig packExtCacheConfig : packExtConfigs) {
+ DfsBlockCacheTable table = new ClockBlockCacheTable(
+ packExtCacheConfig.getPackExtCacheConfiguration());
+ for (PackExt packExt : packExtCacheConfig.getPackExts()) {
+ if (packExtBlockCacheTables.containsKey(packExt)) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().duplicatePackExtensionsForCacheTables,
+ packExt));
+ }
+ packExtBlockCacheTables.put(packExt, table);
+ }
+ }
+ return fromCacheTables(defaultTable, packExtBlockCacheTables);
+ }
+
+ /**
+ * Creates a new PackExtBlockCacheTable from the combination of a default
+ * {@link DfsBlockCacheTable} and a map of {@link PackExt}s to
+ * {@link DfsBlockCacheTable}s.
+ * <p>
+ * This method allows for the PackExtBlockCacheTable to handle a mapping of
+ * {@link PackExt}s to arbitrarily defined {@link DfsBlockCacheTable}
+ * implementations. This is especially useful for users wishing to implement
+ * custom cache tables.
+ * <p>
+ * This is currently made visible for testing.
+ *
+ * @param defaultBlockCacheTable
+ * the default table used when a handling a {@link PackExt} type
+ * that does not map to a {@link DfsBlockCacheTable} mapped by
+ * packExtsCacheTablePairs.
+ * @param packExtBlockCacheTables
+ * the mapping of {@link PackExt}s to
+ * {@link DfsBlockCacheTable}s. A single
+ * {@link DfsBlockCacheTable} can be defined for multiple
+ * {@link PackExt}s in a many-to-one relationship.
+ * @return the PackExtBlockCacheTable created from the
+ * defaultBlockCacheTable and packExtsCacheTablePairs mapping.
+ * @throws IllegalArgumentException
+ * when a {@link PackExt} is defined for multiple
+ * {@link DfsBlockCacheTable}s.
+ */
+ static PackExtBlockCacheTable fromCacheTables(
+ DfsBlockCacheTable defaultBlockCacheTable,
+ Map<PackExt, DfsBlockCacheTable> packExtBlockCacheTables) {
+ Set<DfsBlockCacheTable> blockCacheTables = new HashSet<>();
+ blockCacheTables.add(defaultBlockCacheTable);
+ blockCacheTables.addAll(packExtBlockCacheTables.values());
+ String name = defaultBlockCacheTable.getName() + "," //$NON-NLS-1$
+ + packExtBlockCacheTables.values().stream()
+ .map(DfsBlockCacheTable::getName).sorted()
+ .collect(Collectors.joining(",")); //$NON-NLS-1$
+ return new PackExtBlockCacheTable(name, defaultBlockCacheTable,
+ List.copyOf(blockCacheTables), packExtBlockCacheTables);
+ }
+
+ private PackExtBlockCacheTable(String name,
+ DfsBlockCacheTable defaultBlockCacheTable,
+ List<DfsBlockCacheTable> blockCacheTableList,
+ Map<PackExt, DfsBlockCacheTable> extBlockCacheTables) {
+ this.name = name;
+ this.defaultBlockCacheTable = defaultBlockCacheTable;
+ this.blockCacheTableList = blockCacheTableList;
+ this.extBlockCacheTables = extBlockCacheTables;
+ }
+
+ @Override
+ public boolean hasBlock0(DfsStreamKey key) {
+ return getTable(key).hasBlock0(key);
+ }
+
+ @Override
+ public DfsBlock getOrLoad(BlockBasedFile file, long position,
+ DfsReader dfsReader, ReadableChannelSupplier fileChannel)
+ throws IOException {
+ return getTable(file.ext).getOrLoad(file, position, dfsReader,
+ fileChannel);
+ }
+
+ @Override
+ public <T> Ref<T> getOrLoadRef(DfsStreamKey key, long position,
+ RefLoader<T> loader) throws IOException {
+ return getTable(key).getOrLoadRef(key, position, loader);
+ }
+
+ @Override
+ public void put(DfsBlock v) {
+ getTable(v.stream).put(v);
+ }
+
+ @Override
+ public <T> Ref<T> put(DfsStreamKey key, long pos, long size, T v) {
+ return getTable(key).put(key, pos, size, v);
+ }
+
+ @Override
+ public <T> Ref<T> putRef(DfsStreamKey key, long size, T v) {
+ return getTable(key).putRef(key, size, v);
+ }
+
+ @Override
+ public boolean contains(DfsStreamKey key, long position) {
+ return getTable(key).contains(key, position);
+ }
+
+ @Override
+ public <T> T get(DfsStreamKey key, long position) {
+ return getTable(key).get(key, position);
+ }
+
+ @Override
+ public List<BlockCacheStats> getBlockCacheStats() {
+ return blockCacheTableList.stream()
+ .flatMap(cacheTable -> cacheTable.getBlockCacheStats().stream())
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ private DfsBlockCacheTable getTable(PackExt packExt) {
+ return extBlockCacheTables.getOrDefault(packExt,
+ defaultBlockCacheTable);
+ }
+
+ private DfsBlockCacheTable getTable(DfsStreamKey key) {
+ return extBlockCacheTables.getOrDefault(getPackExt(key),
+ defaultBlockCacheTable);
+ }
+
+ private static PackExt getPackExt(DfsStreamKey key) {
+ return PackExt.values()[key.packExtPos];
+ }
+}
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/BasePackIndexWriter.java
index 87e0b44d46..b89cc1ebf4 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/BasePackIndexWriter.java
@@ -19,6 +19,7 @@ import java.text.MessageFormat;
import java.util.List;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.PackIndexWriter;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.transport.PackedObjectInfo;
import org.eclipse.jgit.util.NB;
@@ -31,7 +32,7 @@ import org.eclipse.jgit.util.NB;
* random access to any object in the pack by associating an ObjectId to the
* byte offset within the pack where the object's data can be read.
*/
-public abstract class PackIndexWriter {
+public abstract class BasePackIndexWriter implements PackIndexWriter {
/** Magic constant indicating post-version 1 format. */
protected static final byte[] TOC = { -1, 't', 'O', 'c' };
@@ -147,7 +148,7 @@ public abstract class PackIndexWriter {
* the stream this instance outputs to. If not already buffered
* it will be automatically wrapped in a buffered stream.
*/
- protected PackIndexWriter(OutputStream dst) {
+ protected BasePackIndexWriter(OutputStream dst) {
out = new DigestOutputStream(dst instanceof BufferedOutputStream ? dst
: new BufferedOutputStream(dst),
Constants.newMessageDigest());
@@ -172,6 +173,7 @@ public abstract class PackIndexWriter {
* an error occurred while writing to the output stream, or this
* index format cannot store the object data supplied.
*/
+ @Override
public void write(final List<? extends PackedObjectInfo> toStore,
final byte[] packDataChecksum) throws IOException {
entries = toStore;
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 ed2516ddd0..e9782e2e18 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
@@ -16,6 +16,7 @@ import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
import java.io.File;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -23,22 +24,27 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.PackRefsCommand;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.events.RefsChangedEvent;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.reftable.MergedReftable;
import org.eclipse.jgit.internal.storage.reftable.ReftableBatchRefUpdate;
import org.eclipse.jgit.internal.storage.reftable.ReftableDatabase;
import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.RefRename;
@@ -67,15 +73,20 @@ public class FileReftableDatabase extends RefDatabase {
private final FileReftableStack reftableStack;
+ private final AtomicBoolean autoRefresh;
+
FileReftableDatabase(FileRepository repo) throws IOException {
- this(repo, new File(new File(repo.getDirectory(), Constants.REFTABLE),
+ this(repo, new File(new File(repo.getCommonDirectory(), Constants.REFTABLE),
Constants.TABLES_LIST));
}
FileReftableDatabase(FileRepository repo, File refstackName) throws IOException {
this.fileRepository = repo;
+ this.autoRefresh = new AtomicBoolean(repo.getConfig().getBoolean(
+ ConfigConstants.CONFIG_REFTABLE_SECTION,
+ ConfigConstants.CONFIG_KEY_AUTOREFRESH, false));
this.reftableStack = new FileReftableStack(refstackName,
- new File(fileRepository.getDirectory(), Constants.REFTABLE),
+ new File(fileRepository.getCommonDirectory(), Constants.REFTABLE),
() -> fileRepository.fireEvent(new RefsChangedEvent()),
() -> fileRepository.getConfig());
this.reftableDatabase = new ReftableDatabase() {
@@ -87,7 +98,13 @@ public class FileReftableDatabase extends RefDatabase {
};
}
- ReflogReader getReflogReader(String refname) throws IOException {
+ @Override
+ public ReflogReader getReflogReader(Ref ref) throws IOException {
+ return reftableDatabase.getReflogReader(ref.getName());
+ }
+
+ @Override
+ public ReflogReader getReflogReader(String refname) throws IOException {
return reftableDatabase.getReflogReader(refname);
}
@@ -108,6 +125,22 @@ public class FileReftableDatabase extends RefDatabase {
}
/**
+ * {@inheritDoc}
+ *
+ * For Reftable, all the data is compacted into a single table.
+ */
+ @Override
+ public void packRefs(ProgressMonitor pm, PackRefsCommand packRefs)
+ throws IOException {
+ pm.beginTask(JGitText.get().packRefs, 1);
+ try {
+ compactFully();
+ } finally {
+ pm.endTask();
+ }
+ }
+
+ /**
* Runs a full compaction for GC purposes.
* @throws IOException on I/O errors
*/
@@ -158,6 +191,7 @@ public class FileReftableDatabase extends RefDatabase {
@Override
public Ref exactRef(String name) throws IOException {
+ autoRefresh();
return reftableDatabase.exactRef(name);
}
@@ -168,6 +202,7 @@ public class FileReftableDatabase extends RefDatabase {
@Override
public Map<String, Ref> getRefs(String prefix) throws IOException {
+ autoRefresh();
List<Ref> refs = reftableDatabase.getRefsByPrefix(prefix);
RefList.Builder<Ref> builder = new RefList.Builder<>(refs.size());
for (Ref r : refs) {
@@ -180,6 +215,7 @@ public class FileReftableDatabase extends RefDatabase {
@Override
public List<Ref> getRefsByPrefixWithExclusions(String include, Set<String> excludes)
throws IOException {
+ autoRefresh();
return reftableDatabase.getRefsByPrefixWithExclusions(include, excludes);
}
@@ -198,6 +234,50 @@ public class FileReftableDatabase extends RefDatabase {
}
+ /**
+ * Whether to auto-refresh the reftable stack if it is out of date.
+ *
+ * @param autoRefresh
+ * whether to auto-refresh the reftable stack if it is out of
+ * date.
+ */
+ public void setAutoRefresh(boolean autoRefresh) {
+ this.autoRefresh.set(autoRefresh);
+ }
+
+ /**
+ * Whether the reftable stack is auto-refreshed if it is out of date.
+ *
+ * @return whether the reftable stack is auto-refreshed if it is out of
+ * date.
+ */
+ public boolean isAutoRefresh() {
+ return autoRefresh.get();
+ }
+
+ private void autoRefresh() {
+ if (autoRefresh.get()) {
+ refresh();
+ }
+ }
+
+ /**
+ * Check if the reftable stack is up to date, and if not, reload it.
+ * <p>
+ * {@inheritDoc}
+ */
+ @Override
+ public void refresh() {
+ try {
+ if (!reftableStack.isUpToDate()) {
+ reftableDatabase.clearCache();
+ reftableStack.reload();
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
private Ref doPeel(Ref leaf) throws IOException {
try (RevWalk rw = new RevWalk(fileRepository)) {
RevObject obj = rw.parseAny(leaf.getObjectId());
@@ -318,7 +398,7 @@ public class FileReftableDatabase extends RefDatabase {
@Override
public void create() throws IOException {
FileUtils.mkdir(
- new File(fileRepository.getDirectory(), Constants.REFTABLE),
+ new File(fileRepository.getCommonDirectory(), Constants.REFTABLE),
true);
}
@@ -538,9 +618,10 @@ public class FileReftableDatabase extends RefDatabase {
boolean writeLogs) throws IOException {
int size = 0;
List<Ref> refs = repo.getRefDatabase().getRefs();
+ RefDatabase refDb = repo.getRefDatabase();
if (writeLogs) {
for (Ref r : refs) {
- ReflogReader rlr = repo.getReflogReader(r.getName());
+ ReflogReader rlr = refDb.getReflogReader(r);
if (rlr != null) {
size = Math.max(rlr.getReverseEntries().size(), size);
}
@@ -563,10 +644,7 @@ public class FileReftableDatabase extends RefDatabase {
if (writeLogs) {
for (Ref r : refs) {
long idx = size;
- ReflogReader reader = repo.getReflogReader(r.getName());
- if (reader == null) {
- continue;
- }
+ ReflogReader reader = refDb.getReflogReader(r);
for (ReflogEntry e : reader.getReverseEntries()) {
w.writeLog(r.getName(), idx, e.getWho(), e.getOldId(),
e.getNewId(), e.getComment());
@@ -615,7 +693,7 @@ public class FileReftableDatabase extends RefDatabase {
FileReftableDatabase newDb = null;
File reftableList = null;
try {
- File reftableDir = new File(repo.getDirectory(),
+ File reftableDir = new File(repo.getCommonDirectory(),
Constants.REFTABLE);
reftableList = new File(reftableDir, Constants.TABLES_LIST);
if (!reftableDir.isDirectory()) {
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 0f5ff0f9f7..b2c88922b8 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
@@ -18,8 +18,10 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.StandardCopyOption;
import java.security.SecureRandom;
import java.util.ArrayList;
@@ -27,6 +29,7 @@ import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -39,6 +42,8 @@ import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.TrustStat;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.SystemReader;
@@ -59,6 +64,9 @@ public class FileReftableStack implements AutoCloseable {
private List<StackEntry> stack;
+ private AtomicReference<FileSnapshot> snapshot = new AtomicReference<>(
+ FileSnapshot.DIRTY);
+
private long lastNextUpdateIndex;
private final File stackPath;
@@ -98,6 +106,8 @@ public class FileReftableStack implements AutoCloseable {
private final CompactionStats stats;
+ private final TrustStat trustTablesListStat;
+
/**
* Creates a stack corresponding to the list of reftables in the argument
*
@@ -126,6 +136,8 @@ public class FileReftableStack implements AutoCloseable {
reload();
stats = new CompactionStats();
+ trustTablesListStat = configSupplier.get().get(CoreConfig.KEY)
+ .getTrustTablesListStat();
}
CompactionStats getStats() {
@@ -272,8 +284,9 @@ public class FileReftableStack implements AutoCloseable {
}
private List<String> readTableNames() throws IOException {
+ FileSnapshot old;
List<String> names = new ArrayList<>(stack.size() + 1);
-
+ old = snapshot.get();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(stackPath), UTF_8))) {
String line;
@@ -282,8 +295,10 @@ public class FileReftableStack implements AutoCloseable {
names.add(line);
}
}
+ snapshot.compareAndSet(old, FileSnapshot.save(stackPath));
} catch (FileNotFoundException e) {
// file isn't there: empty repository.
+ snapshot.compareAndSet(old, FileSnapshot.MISSING_FILE);
}
return names;
}
@@ -294,9 +309,28 @@ public class FileReftableStack implements AutoCloseable {
* on IO problem
*/
boolean isUpToDate() throws IOException {
- // We could use FileSnapshot to avoid reading the file, but the file is
- // small so it's probably a minor optimization.
try {
+ switch (trustTablesListStat) {
+ case NEVER:
+ break;
+ case AFTER_OPEN:
+ try (InputStream stream = Files
+ .newInputStream(stackPath.toPath())) {
+ // open the tables.list file to refresh attributes (on some
+ // NFS clients)
+ } catch (FileNotFoundException | NoSuchFileException e) {
+ // ignore
+ }
+ //$FALL-THROUGH$
+ case ALWAYS:
+ if (!snapshot.get().isModified(stackPath)) {
+ return true;
+ }
+ break;
+ case INHERIT:
+ // only used in CoreConfig internally
+ throw new IllegalStateException();
+ }
List<String> names = readTableNames();
if (names.size() != stack.size()) {
return false;
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 e5a00d3925..bcf9f1efdf 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
@@ -2,7 +2,7 @@
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
* Copyright (C) 2008-2010, Google Inc.
* Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2006-2024, 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,8 +31,8 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Set;
-import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.attributes.AttributesNode;
import org.eclipse.jgit.attributes.AttributesNodeProvider;
@@ -60,7 +60,6 @@ import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
-import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.IO;
@@ -165,7 +164,7 @@ public class FileRepository extends Repository {
throw new IOException(e.getMessage(), e);
}
repoConfig = new FileBasedConfig(userConfig, getFS().resolve(
- getDirectory(), Constants.CONFIG),
+ getCommonDirectory(), Constants.CONFIG),
getFS());
loadRepoConfig();
@@ -193,7 +192,7 @@ public class FileRepository extends Repository {
options.getObjectDirectory(), //
options.getAlternateObjectDirectories(), //
getFS(), //
- new File(getDirectory(), Constants.SHALLOW));
+ new File(getCommonDirectory(), Constants.SHALLOW));
if (objectDatabase.exists()) {
if (repositoryFormatVersion > 1)
@@ -215,6 +214,16 @@ public class FileRepository extends Repository {
}
}
+ private String getRelativeDir(File base, File other) {
+ File relPath;
+ try {
+ relPath = base.toPath().relativize(other.toPath()).toFile();
+ } catch (IllegalArgumentException e) {
+ relPath = other;
+ }
+ return FileUtils.pathToString(relPath);
+ }
+
/**
* {@inheritDoc}
* <p>
@@ -223,6 +232,22 @@ public class FileRepository extends Repository {
*/
@Override
public void create(boolean bare) throws IOException {
+ create(bare, false);
+ }
+
+ /**
+ * Create a new Git repository initializing the necessary files and
+ * directories.
+ *
+ * @param bare
+ * if true, a bare repository (a repository without a working
+ * directory) is created.
+ * @param relativePaths
+ * if true, relative paths are used for GIT_DIR and GIT_WORK_TREE
+ * @throws IOException
+ * in case of IO problem
+ */
+ public void create(boolean bare, boolean relativePaths) throws IOException {
final FileBasedConfig cfg = getConfig();
if (cfg.getFile().exists()) {
throw new IllegalStateException(MessageFormat.format(
@@ -293,15 +318,25 @@ public class FileRepository extends Repository {
if (!bare) {
File workTree = getWorkTree();
if (!getDirectory().getParentFile().equals(workTree)) {
+ String workTreePath;
+ String gitDirPath;
+ if (relativePaths) {
+ File canonGitDir = getDirectory().getCanonicalFile();
+ File canonWorkTree = getWorkTree().getCanonicalFile();
+ workTreePath = getRelativeDir(canonGitDir, canonWorkTree);
+ gitDirPath = getRelativeDir(canonWorkTree, canonGitDir);
+ } else {
+ workTreePath = getWorkTree().getAbsolutePath();
+ gitDirPath = getDirectory().getAbsolutePath();
+ }
cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_WORKTREE, getWorkTree()
- .getAbsolutePath());
+ ConfigConstants.CONFIG_KEY_WORKTREE, workTreePath);
LockFile dotGitLockFile = new LockFile(new File(workTree,
Constants.DOT_GIT));
try {
if (dotGitLockFile.lock()) {
dotGitLockFile.write(Constants.encode(Constants.GITDIR
- + getDirectory().getAbsolutePath()));
+ + gitDirPath));
dotGitLockFile.commit();
}
} finally {
@@ -507,29 +542,6 @@ public class FileRepository extends Repository {
}
@Override
- public ReflogReader getReflogReader(String refName) throws IOException {
- if (refs instanceof FileReftableDatabase) {
- // Cannot use findRef: reftable stores log data for deleted or renamed
- // branches.
- return ((FileReftableDatabase)refs).getReflogReader(refName);
- }
-
- // TODO: use exactRef here, which offers more predictable and therefore preferable
- // behavior.
- Ref ref = findRef(refName);
- if (ref == null) {
- return null;
- }
- return new ReflogReaderImpl(this, ref.getName());
- }
-
- @Override
- public @NonNull ReflogReader getReflogReader(@NonNull Ref ref)
- throws IOException {
- return new ReflogReaderImpl(this, ref.getName());
- }
-
- @Override
public AttributesNodeProvider createAttributesNodeProvider() {
return new AttributesNodeProviderImpl(this);
}
@@ -595,13 +607,12 @@ public class FileRepository extends Repository {
@Override
public void autoGC(ProgressMonitor monitor) {
GC gc = new GC(this);
- gc.setPackConfig(new PackConfig(this));
gc.setProgressMonitor(monitor);
gc.setAuto(true);
gc.setBackground(shouldAutoDetach());
try {
gc.gc();
- } catch (ParseException | IOException e) {
+ } catch (ParseException | IOException | GitAPIException e) {
throw new JGitInternalException(JGitText.get().gcFailed, e);
}
}
@@ -622,16 +633,17 @@ public class FileRepository extends Repository {
* on IO problem
*/
void convertToPackedRefs(boolean writeLogs, boolean backup) throws IOException {
+ File commonDirectory = getCommonDirectory();
List<Ref> all = refs.getRefs();
- File packedRefs = new File(getDirectory(), Constants.PACKED_REFS);
+ File packedRefs = new File(commonDirectory, Constants.PACKED_REFS);
if (packedRefs.exists()) {
throw new IOException(MessageFormat.format(JGitText.get().fileAlreadyExists,
packedRefs.getName()));
}
- File refsFile = new File(getDirectory(), "refs"); //$NON-NLS-1$
+ File refsFile = new File(commonDirectory, "refs"); //$NON-NLS-1$
File refsHeadsFile = new File(refsFile, "heads");//$NON-NLS-1$
- File headFile = new File(getDirectory(), Constants.HEAD);
+ File headFile = new File(commonDirectory, Constants.HEAD);
FileReftableDatabase oldDb = (FileReftableDatabase) refs;
// Remove the dummy files that ensure compatibility with older git
@@ -661,8 +673,8 @@ public class FileRepository extends Repository {
}
if (writeLogs) {
- List<ReflogEntry> logs = oldDb.getReflogReader(r.getName())
- .getReverseEntries();
+ ReflogReader reflogReader = oldDb.getReflogReader(r);
+ List<ReflogEntry> logs = reflogReader.getReverseEntries();
Collections.reverse(logs);
for (ReflogEntry e : logs) {
logWriter.log(r.getName(), e);
@@ -701,12 +713,14 @@ public class FileRepository extends Repository {
}
if (!backup) {
- File reftableDir = new File(getDirectory(), Constants.REFTABLE);
+ File reftableDir = new File(commonDirectory, Constants.REFTABLE);
FileUtils.delete(reftableDir,
FileUtils.RECURSIVE | FileUtils.IGNORE_ERRORS);
}
repoConfig.unset(ConfigConstants.CONFIG_EXTENSIONS_SECTION, null,
ConfigConstants.CONFIG_KEY_REF_STORAGE);
+ repoConfig.setLong(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 0);
repoConfig.save();
}
@@ -730,8 +744,10 @@ public class FileRepository extends Repository {
@SuppressWarnings("nls")
void convertToReftable(boolean writeLogs, boolean backup)
throws IOException {
- File reftableDir = new File(getDirectory(), Constants.REFTABLE);
- File headFile = new File(getDirectory(), Constants.HEAD);
+ File commonDirectory = getCommonDirectory();
+ File directory = getDirectory();
+ File reftableDir = new File(commonDirectory, Constants.REFTABLE);
+ File headFile = new File(directory, Constants.HEAD);
if (reftableDir.exists() && FileUtils.hasFiles(reftableDir.toPath())) {
throw new IOException(JGitText.get().reftableDirExists);
}
@@ -739,28 +755,28 @@ public class FileRepository extends Repository {
// Ignore return value, as it is tied to temporary newRefs file.
FileReftableDatabase.convertFrom(this, writeLogs);
- File refsFile = new File(getDirectory(), "refs");
+ File refsFile = new File(commonDirectory, "refs");
// non-atomic: remove old data.
- File packedRefs = new File(getDirectory(), Constants.PACKED_REFS);
- File logsDir = new File(getDirectory(), Constants.LOGS);
+ File packedRefs = new File(commonDirectory, Constants.PACKED_REFS);
+ File logsDir = new File(commonDirectory, Constants.LOGS);
List<String> additional = getRefDatabase().getAdditionalRefs().stream()
.map(Ref::getName).collect(toList());
additional.add(Constants.HEAD);
if (backup) {
- FileUtils.rename(refsFile, new File(getDirectory(), "refs.old"));
+ FileUtils.rename(refsFile, new File(commonDirectory, "refs.old"));
if (packedRefs.exists()) {
- FileUtils.rename(packedRefs, new File(getDirectory(),
+ FileUtils.rename(packedRefs, new File(commonDirectory,
Constants.PACKED_REFS + ".old"));
}
if (logsDir.exists()) {
FileUtils.rename(logsDir,
- new File(getDirectory(), Constants.LOGS + ".old"));
+ new File(commonDirectory, Constants.LOGS + ".old"));
}
for (String r : additional) {
- FileUtils.rename(new File(getDirectory(), r),
- new File(getDirectory(), r + ".old"));
+ FileUtils.rename(new File(commonDirectory, r),
+ new File(commonDirectory, r + ".old"));
}
} else {
FileUtils.delete(packedRefs, FileUtils.SKIP_MISSING);
@@ -770,7 +786,7 @@ public class FileRepository extends Repository {
FileUtils.delete(refsFile,
FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
for (String r : additional) {
- new File(getDirectory(), r).delete();
+ new File(commonDirectory, r).delete();
}
}
@@ -784,7 +800,7 @@ public class FileRepository extends Repository {
// Some tools might write directly into .git/refs/heads/BRANCH. By
// putting a file here, this fails spectacularly.
- FileUtils.createNewFile(new File(refsFile, "heads"));
+ FileUtils.createNewFile(new File(refsFile, Constants.HEADS));
repoConfig.setString(ConfigConstants.CONFIG_EXTENSIONS_SECTION, null,
ConfigConstants.CONFIG_KEY_REF_STORAGE,
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 c88ac984ec..b6bde6eea0 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
@@ -15,6 +15,7 @@ import static org.eclipse.jgit.util.FS.FileStoreAttributes.FALLBACK_TIMESTAMP_RE
import java.io.File;
import java.io.IOException;
+import java.nio.file.FileSystemException;
import java.nio.file.NoSuchFileException;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Duration;
@@ -22,10 +23,12 @@ import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
+import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FS.FileStoreAttributes;
import org.slf4j.Logger;
@@ -139,29 +142,6 @@ public class FileSnapshot {
* @param modified
* the last modification time of the file
* @return the snapshot.
- * @deprecated use {@link #save(Instant)} instead.
- */
- @Deprecated
- public static FileSnapshot save(long modified) {
- final Instant read = Instant.now();
- return new FileSnapshot(read, Instant.ofEpochMilli(modified),
- UNKNOWN_SIZE, FALLBACK_TIMESTAMP_RESOLUTION, MISSING_FILEKEY);
- }
-
- /**
- * Record a snapshot for a file for which the last modification time is
- * already known.
- * <p>
- * This method should be invoked before the file is accessed.
- * <p>
- * Note that this method cannot rely on measuring file timestamp resolution
- * to avoid racy git issues caused by finite file timestamp resolution since
- * it's unknown in which filesystem the file is located. Hence the worst
- * case fallback for timestamp resolution is used.
- *
- * @param modified
- * the last modification time of the file
- * @return the snapshot.
*/
public static FileSnapshot save(Instant modified) {
final Instant read = Instant.now();
@@ -231,14 +211,8 @@ public class FileSnapshot {
this.useConfig = useConfig;
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) {
- LOG.error(e.getMessage(), e);
+ fileAttributes = getFileAttributes(file);
+ } catch (NoSuchElementException e) {
this.lastModified = Instant.EPOCH;
this.size = 0L;
this.fileKey = MISSING_FILEKEY;
@@ -282,17 +256,6 @@ public class FileSnapshot {
* Get time of last snapshot update
*
* @return time of last snapshot update
- * @deprecated use {@link #lastModifiedInstant()} instead
- */
- @Deprecated
- public long lastModified() {
- return lastModified.toEpochMilli();
- }
-
- /**
- * Get time of last snapshot update
- *
- * @return time of last snapshot update
*/
public Instant lastModifiedInstant() {
return lastModified;
@@ -319,16 +282,11 @@ public class FileSnapshot {
long currSize;
Object currFileKey;
try {
- BasicFileAttributes fileAttributes = FS.DETECTED.fileAttributes(path);
+ BasicFileAttributes fileAttributes = getFileAttributes(path);
currLastModified = fileAttributes.lastModifiedTime().toInstant();
currSize = fileAttributes.size();
currFileKey = getFileKey(fileAttributes);
- } catch (NoSuchFileException e) {
- currLastModified = Instant.EPOCH;
- currSize = 0L;
- currFileKey = MISSING_FILEKEY;
- } catch (IOException e) {
- LOG.error(e.getMessage(), e);
+ } catch (NoSuchElementException e) {
currLastModified = Instant.EPOCH;
currSize = 0L;
currFileKey = MISSING_FILEKEY;
@@ -586,4 +544,27 @@ public class FileSnapshot {
}
return fileStoreAttributeCache;
}
+
+ private static BasicFileAttributes getFileAttributes(File path)
+ throws NoSuchElementException {
+ try {
+ try {
+ return FS.DETECTED.fileAttributes(path);
+ } catch (IOException e) {
+ if (!FileUtils.isStaleFileHandle(e)) {
+ throw e;
+ }
+ }
+ } catch (NoSuchFileException e) {
+ // ignore
+ } catch (FileSystemException e) {
+ String msg = e.getMessage();
+ if (!msg.endsWith("Not a directory")) { //$NON-NLS-1$
+ LOG.error(msg, e);
+ }
+ } catch (IOException e) {
+ LOG.error(e.getMessage(), e);
+ }
+ throw new NoSuchElementException(path.toString());
+ }
}
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 cf26f8d284..05bd970789 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
@@ -63,6 +63,8 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.PackRefsCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.CancelledException;
import org.eclipse.jgit.errors.CorruptObjectException;
@@ -100,9 +102,8 @@ import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FS.LockToken;
import org.eclipse.jgit.util.FileUtils;
-import org.eclipse.jgit.util.GitDateParser;
+import org.eclipse.jgit.util.GitTimeParser;
import org.eclipse.jgit.util.StringUtils;
-import org.eclipse.jgit.util.SystemReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -158,11 +159,11 @@ public class GC {
private long expireAgeMillis = -1;
- private Date expire;
+ private Instant expire;
private long packExpireAgeMillis = -1;
- private Date packExpire;
+ private Instant packExpire;
private Boolean packKeptObjects;
@@ -233,9 +234,11 @@ public class GC {
* @throws java.text.ParseException
* If the configuration parameter "gc.pruneexpire" couldn't be
* parsed
+ * @throws GitAPIException
+ * If packing refs failed
*/
public CompletableFuture<Collection<Pack>> gc()
- throws IOException, ParseException {
+ throws IOException, ParseException, GitAPIException {
if (!background) {
return CompletableFuture.completedFuture(doGc());
}
@@ -254,7 +257,7 @@ public class GC {
gcLog.commit();
}
return newPacks;
- } catch (IOException | ParseException e) {
+ } catch (IOException | ParseException | GitAPIException e) {
try {
gcLog.write(e.getMessage());
StringWriter sw = new StringWriter();
@@ -277,7 +280,8 @@ public class GC {
return (executor != null) ? executor : WorkQueue.getExecutor();
}
- private Collection<Pack> doGc() throws IOException, ParseException {
+ private Collection<Pack> doGc()
+ throws IOException, ParseException, GitAPIException {
if (automatic && !needGc()) {
return Collections.emptyList();
}
@@ -286,7 +290,8 @@ public class GC {
return Collections.emptyList();
}
pm.start(6 /* tasks */);
- packRefs();
+ new PackRefsCommand(repo).setProgressMonitor(pm).setAll(true)
+ .call();
// TODO: implement reflog_expire(pm, repo);
Collection<Pack> newPacks = repack();
prune(Collections.emptySet());
@@ -692,16 +697,18 @@ public class GC {
if (expire == null && expireAgeMillis == -1) {
String pruneExpireStr = getPruneExpireStr();
- if (pruneExpireStr == null)
+ if (pruneExpireStr == null) {
pruneExpireStr = PRUNE_EXPIRE_DEFAULT;
- expire = GitDateParser.parse(pruneExpireStr, null, SystemReader
- .getInstance().getLocale());
+ }
+ expire = GitTimeParser.parseInstant(pruneExpireStr);
expireAgeMillis = -1;
}
- if (expire != null)
- expireDate = expire.getTime();
- if (expireAgeMillis != -1)
+ if (expire != null) {
+ expireDate = expire.toEpochMilli();
+ }
+ if (expireAgeMillis != -1) {
expireDate = System.currentTimeMillis() - expireAgeMillis;
+ }
return expireDate;
}
@@ -718,16 +725,18 @@ public class GC {
String prunePackExpireStr = repo.getConfig().getString(
ConfigConstants.CONFIG_GC_SECTION, null,
ConfigConstants.CONFIG_KEY_PRUNEPACKEXPIRE);
- if (prunePackExpireStr == null)
+ if (prunePackExpireStr == null) {
prunePackExpireStr = PRUNE_PACK_EXPIRE_DEFAULT;
- packExpire = GitDateParser.parse(prunePackExpireStr, null,
- SystemReader.getInstance().getLocale());
+ }
+ packExpire = GitTimeParser.parseInstant(prunePackExpireStr);
packExpireAgeMillis = -1;
}
- if (packExpire != null)
- packExpireDate = packExpire.getTime();
- if (packExpireAgeMillis != -1)
+ if (packExpire != null) {
+ packExpireDate = packExpire.toEpochMilli();
+ }
+ if (packExpireAgeMillis != -1) {
packExpireDate = System.currentTimeMillis() - packExpireAgeMillis;
+ }
return packExpireDate;
}
@@ -780,43 +789,6 @@ public class GC {
}
/**
- * Pack ref storage. For a RefDirectory database, this packs all
- * non-symbolic, loose refs into packed-refs. For Reftable, all of the data
- * is compacted into a single table.
- *
- * @throws java.io.IOException
- * if an IO error occurred
- */
- public void packRefs() throws IOException {
- RefDatabase refDb = repo.getRefDatabase();
- if (refDb instanceof FileReftableDatabase) {
- // TODO: abstract this more cleanly.
- pm.beginTask(JGitText.get().packRefs, 1);
- try {
- ((FileReftableDatabase) refDb).compactFully();
- } finally {
- pm.endTask();
- }
- return;
- }
-
- Collection<Ref> refs = refDb.getRefsByPrefix(Constants.R_REFS);
- List<String> refsToBePacked = new ArrayList<>(refs.size());
- pm.beginTask(JGitText.get().packRefs, refs.size());
- try {
- for (Ref ref : refs) {
- checkCancelled();
- if (!ref.isSymbolic() && ref.getStorage().isLoose())
- refsToBePacked.add(ref.getName());
- pm.update(1);
- }
- ((RefDirectory) repo.getRefDatabase()).pack(refsToBePacked);
- } finally {
- pm.endTask();
- }
- }
-
- /**
* Packs all objects which reachable from any of the heads into one pack
* file. Additionally all objects which are not reachable from any head but
* which are reachable from any of the other refs (e.g. tags), special refs
@@ -1047,7 +1019,7 @@ public class GC {
}
private void deleteEmptyRefsFolders() throws IOException {
- Path refs = repo.getDirectory().toPath().resolve(Constants.R_REFS);
+ Path refs = repo.getCommonDirectory().toPath().resolve(Constants.R_REFS);
// Avoid deleting a folder that was created after the threshold so that concurrent
// operations trying to create a reference are not impacted
Instant threshold = Instant.now().minus(30, ChronoUnit.SECONDS);
@@ -1185,7 +1157,7 @@ public class GC {
* if an IO error occurred
*/
private Set<ObjectId> listRefLogObjects(Ref ref, long minTime) throws IOException {
- ReflogReader reflogReader = repo.getReflogReader(ref);
+ ReflogReader reflogReader = repo.getRefDatabase().getReflogReader(ref);
List<ReflogEntry> rlEntries = reflogReader
.getReverseEntries();
if (rlEntries == null || rlEntries.isEmpty())
@@ -1509,6 +1481,18 @@ public class GC {
public long numberOfPackFiles;
/**
+ * The number of pack files that were created since the last bitmap
+ * generation.
+ */
+ public long numberOfPackFilesSinceBitmap;
+
+ /**
+ * The number of objects stored in pack files and as loose object
+ * created after the last bitmap generation.
+ */
+ public long numberOfObjectsSinceBitmap;
+
+ /**
* The number of objects stored as loose objects.
*/
public long numberOfLooseObjects;
@@ -1543,6 +1527,10 @@ public class GC {
final StringBuilder b = new StringBuilder();
b.append("numberOfPackedObjects=").append(numberOfPackedObjects); //$NON-NLS-1$
b.append(", numberOfPackFiles=").append(numberOfPackFiles); //$NON-NLS-1$
+ b.append(", numberOfPackFilesSinceBitmap=") //$NON-NLS-1$
+ .append(numberOfPackFilesSinceBitmap);
+ b.append(", numberOfObjectsSinceBitmap=") //$NON-NLS-1$
+ .append(numberOfObjectsSinceBitmap);
b.append(", numberOfLooseObjects=").append(numberOfLooseObjects); //$NON-NLS-1$
b.append(", numberOfLooseRefs=").append(numberOfLooseRefs); //$NON-NLS-1$
b.append(", numberOfPackedRefs=").append(numberOfPackedRefs); //$NON-NLS-1$
@@ -1563,12 +1551,22 @@ public class GC {
public RepoStatistics getStatistics() throws IOException {
RepoStatistics ret = new RepoStatistics();
Collection<Pack> packs = repo.getObjectDatabase().getPacks();
+ long latestBitmapTime = 0L;
for (Pack p : packs) {
- ret.numberOfPackedObjects += p.getIndex().getObjectCount();
+ long packedObjects = p.getIndex().getObjectCount();
+ ret.numberOfPackedObjects += packedObjects;
ret.numberOfPackFiles++;
ret.sizeOfPackedObjects += p.getPackFile().length();
- if (p.getBitmapIndex() != null)
+ if (p.getBitmapIndex() != null) {
ret.numberOfBitmaps += p.getBitmapIndex().getBitmapCount();
+ if (latestBitmapTime == 0L) {
+ latestBitmapTime = p.getFileSnapshot().lastModifiedInstant().toEpochMilli();
+ }
+ }
+ else if (latestBitmapTime == 0L) {
+ ret.numberOfPackFilesSinceBitmap++;
+ ret.numberOfObjectsSinceBitmap += packedObjects;
+ }
}
File objDir = repo.getObjectsDirectory();
String[] fanout = objDir.list();
@@ -1584,6 +1582,9 @@ public class GC {
continue;
ret.numberOfLooseObjects++;
ret.sizeOfLooseObjects += f.length();
+ if (f.lastModified() > latestBitmapTime) {
+ ret.numberOfObjectsSinceBitmap ++;
+ }
}
}
}
@@ -1659,12 +1660,31 @@ public class GC {
* candidate for pruning.
*
* @param expire
- * instant in time which defines object expiration
- * objects with modification time before this instant are expired
- * objects with modification time newer or equal to this instant
- * are not expired
+ * instant in time which defines object expiration objects with
+ * modification time before this instant are expired objects with
+ * modification time newer or equal to this instant are not
+ * expired
+ * @deprecated use {@link #setExpire(Instant)} instead
*/
+ @Deprecated(since = "7.2")
public void setExpire(Date expire) {
+ this.expire = expire.toInstant();
+ expireAgeMillis = -1;
+ }
+
+ /**
+ * During gc() or prune() each unreferenced, loose object which has been
+ * created or modified after or at <code>expire</code> will not be pruned.
+ * Only older objects may be pruned. If set to null then every object is a
+ * candidate for pruning.
+ *
+ * @param expire
+ * instant in time which defines object expiration objects with
+ * modification time before this instant are expired objects with
+ * modification time newer or equal to this instant are not
+ * expired
+ */
+ public void setExpire(Instant expire) {
this.expire = expire;
expireAgeMillis = -1;
}
@@ -1677,8 +1697,24 @@ public class GC {
*
* @param packExpire
* instant in time which defines packfile expiration
+ * @deprecated use {@link #setPackExpire(Instant)} instead
*/
+ @Deprecated(since = "7.2")
public void setPackExpire(Date packExpire) {
+ this.packExpire = packExpire.toInstant();
+ packExpireAgeMillis = -1;
+ }
+
+ /**
+ * During gc() or prune() packfiles which are created or modified after or
+ * at <code>packExpire</code> will not be deleted. Only older packfiles may
+ * be deleted. If set to null then every packfile is a candidate for
+ * deletion.
+ *
+ * @param packExpire
+ * instant in time which defines packfile expiration
+ */
+ public void setPackExpire(Instant packExpire) {
this.packExpire = packExpire;
packExpireAgeMillis = -1;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java
index 628bf5db0c..862aaab0ee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java
@@ -23,8 +23,7 @@ import java.time.Instant;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.util.FileUtils;
-import org.eclipse.jgit.util.GitDateParser;
-import org.eclipse.jgit.util.SystemReader;
+import org.eclipse.jgit.util.GitTimeParser;
/**
* This class manages the gc.log file for a {@link FileRepository}.
@@ -50,7 +49,7 @@ class GcLog {
*/
GcLog(FileRepository repo) {
this.repo = repo;
- logFile = new File(repo.getDirectory(), "gc.log"); //$NON-NLS-1$
+ logFile = new File(repo.getCommonDirectory(), "gc.log"); //$NON-NLS-1$
lock = new LockFile(logFile);
}
@@ -62,8 +61,7 @@ class GcLog {
if (logExpiryStr == null) {
logExpiryStr = LOG_EXPIRY_DEFAULT;
}
- gcLogExpire = GitDateParser.parse(logExpiryStr, null,
- SystemReader.getInstance().getLocale()).toInstant();
+ gcLogExpire = GitTimeParser.parseInstant(logExpiryStr);
}
return gcLogExpire;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
index 11d842b246..e8d442b8fb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
@@ -46,7 +46,7 @@ public class InfoAttributesNode extends AttributesNode {
FS fs = repository.getFS();
- File attributes = fs.resolve(repository.getDirectory(),
+ File attributes = fs.resolve(repository.getCommonDirectory(),
Constants.INFO_ATTRIBUTES);
FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributes);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
index a2d8bd0140..9e12ee8a0c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
@@ -24,6 +24,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.FileTime;
import java.text.MessageFormat;
@@ -141,9 +142,8 @@ public class LockFile {
throw new IllegalStateException(
MessageFormat.format(JGitText.get().lockAlreadyHeld, ref));
}
- FileUtils.mkdirs(lck.getParentFile(), true);
try {
- token = FS.DETECTED.createNewFileAtomic(lck);
+ token = createLockFileWithRetry();
} catch (IOException e) {
LOG.error(JGitText.get().failedCreateLockFile, lck, e);
throw e;
@@ -160,6 +160,19 @@ public class LockFile {
return obtainedLock;
}
+ private FS.LockToken createLockFileWithRetry() throws IOException {
+ try {
+ return createLockFile();
+ } catch (NoSuchFileException e) {
+ return createLockFile();
+ }
+ }
+
+ private FS.LockToken createLockFile() throws IOException {
+ FileUtils.mkdirs(lck.getParentFile(), true);
+ return FS.DETECTED.createNewFileAtomic(lck);
+ }
+
/**
* Try to establish the lock for appending.
*
@@ -515,17 +528,6 @@ public class LockFile {
* Get the modification time of the output file when it was committed.
*
* @return modification time of the lock file right before we committed it.
- * @deprecated use {@link #getCommitLastModifiedInstant()} instead
- */
- @Deprecated
- public long getCommitLastModified() {
- return commitSnapshot.lastModified();
- }
-
- /**
- * Get the modification time of the output file when it was committed.
- *
- * @return modification time of the lock file right before we committed it.
*/
public Instant getCommitLastModifiedInstant() {
return commitSnapshot.lastModifiedInstant();
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
index b4bb2a9293..909b3e3082 100644
--- 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
@@ -26,8 +26,9 @@ import org.eclipse.jgit.internal.storage.file.FileObjectDatabase.InsertLooseObje
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.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.TrustStat;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.util.FileUtils;
@@ -49,13 +50,13 @@ class LooseObjects {
* Maximum number of attempts to read a loose object for which a stale file
* handle exception is thrown
*/
- private final static int MAX_LOOSE_OBJECT_STALE_READ_ATTEMPTS = 5;
+ private final static int MAX_STALE_READ_RETRIES = 5;
private final File directory;
private final UnpackedObjectCache unpackedObjectCache;
- private final boolean trustFolderStat;
+ private final TrustStat trustLooseObjectStat;
/**
* Initialize a reference to an on-disk object directory.
@@ -68,9 +69,8 @@ class LooseObjects {
LooseObjects(Config config, File dir) {
directory = dir;
unpackedObjectCache = new UnpackedObjectCache();
- trustFolderStat = config.getBoolean(
- ConfigConstants.CONFIG_CORE_SECTION,
- ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
+ trustLooseObjectStat = config.get(CoreConfig.KEY)
+ .getTrustLooseObjectStat();
}
/**
@@ -108,7 +108,8 @@ class LooseObjects {
*/
boolean has(AnyObjectId objectId) {
boolean exists = hasWithoutRefresh(objectId);
- if (trustFolderStat || exists) {
+ if (trustLooseObjectStat == TrustStat.ALWAYS
+ || exists) {
return exists;
}
try (InputStream stream = Files.newInputStream(directory.toPath())) {
@@ -163,13 +164,31 @@ class LooseObjects {
}
ObjectLoader open(WindowCursor curs, AnyObjectId id) throws IOException {
- int readAttempts = 0;
- while (readAttempts < MAX_LOOSE_OBJECT_STALE_READ_ATTEMPTS) {
- readAttempts++;
- File path = fileFor(id);
- if (trustFolderStat && !path.exists()) {
+ File path = fileFor(id);
+ for (int retries = 0; retries < MAX_STALE_READ_RETRIES; retries++) {
+ boolean reload = true;
+ switch (trustLooseObjectStat) {
+ case NEVER:
break;
+ case AFTER_OPEN:
+ try (InputStream stream = Files
+ .newInputStream(path.getParentFile().toPath())) {
+ // open the loose object's fanout directory to refresh
+ // attributes (on some NFS clients)
+ } catch (FileNotFoundException | NoSuchFileException e) {
+ // ignore
+ }
+ //$FALL-THROUGH$
+ case ALWAYS:
+ if (!path.exists()) {
+ reload = false;
+ }
+ break;
+ case INHERIT:
+ // only used in CoreConfig internally
+ throw new IllegalStateException();
}
+ if (reload) {
try {
return getObjectLoader(curs, path, id);
} catch (FileNotFoundException noFile) {
@@ -183,9 +202,10 @@ class LooseObjects {
}
if (LOG.isDebugEnabled()) {
LOG.debug(MessageFormat.format(
- JGitText.get().looseObjectHandleIsStale, id.name(),
- Integer.valueOf(readAttempts), Integer.valueOf(
- MAX_LOOSE_OBJECT_STALE_READ_ATTEMPTS)));
+ JGitText.get().looseObjectHandleIsStale,
+ id.name(), Integer.valueOf(retries),
+ Integer.valueOf(MAX_STALE_READ_RETRIES)));
+ }
}
}
}
@@ -211,7 +231,7 @@ class LooseObjects {
try {
return getObjectLoaderWithoutRefresh(curs, path, id);
} catch (FileNotFoundException e) {
- if (trustFolderStat) {
+ if (trustLooseObjectStat == TrustStat.ALWAYS) {
throw e;
}
try (InputStream stream = Files
@@ -248,7 +268,7 @@ class LooseObjects {
return getSizeWithoutRefresh(curs, id);
} catch (FileNotFoundException noFile) {
try {
- if (trustFolderStat) {
+ if (trustLooseObjectStat == TrustStat.ALWAYS) {
throw noFile;
}
try (InputStream stream = Files
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 9f27f4bd6e..746e124e1f 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
@@ -28,6 +28,7 @@ import java.util.zip.Deflater;
import org.eclipse.jgit.errors.LockFailedException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackIndexWriter;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig;
@@ -110,7 +111,7 @@ public class ObjectDirectoryPackParser extends PackParser {
* @param version
* the version to write. The special version 0 designates the
* oldest (most compatible) format available for the objects.
- * @see PackIndexWriter
+ * @see BasePackIndexWriter
*/
public void setIndexVersion(int version) {
indexVersion = version;
@@ -386,9 +387,9 @@ public class ObjectDirectoryPackParser extends PackParser {
try (FileOutputStream os = new FileOutputStream(tmpIdx)) {
final PackIndexWriter iw;
if (indexVersion <= 0)
- iw = PackIndexWriter.createOldestPossible(os, list);
+ iw = BasePackIndexWriter.createOldestPossible(os, list);
else
- iw = PackIndexWriter.createVersion(os, indexVersion);
+ iw = BasePackIndexWriter.createVersion(os, indexVersion);
iw.write(list, packHash);
os.getChannel().force(true);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
index f87329ccc2..5813d39e9a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
@@ -95,6 +95,9 @@ public class Pack implements Iterable<PackIndex.MutableEntry> {
private RandomAccessFile fd;
+ /** For managing open/close accounting of {@link #fd}. */
+ private final Object activeLock = new Object();
+
/** Serializes reads performed against {@link #fd}. */
private final Object readLock = new Object();
@@ -113,13 +116,13 @@ public class Pack implements Iterable<PackIndex.MutableEntry> {
private volatile Exception invalidatingCause;
@Nullable
- private PackFile bitmapIdxFile;
+ private volatile PackFile bitmapIdxFile;
private AtomicInteger transientErrorCount = new AtomicInteger();
private byte[] packChecksum;
- private volatile Optionally<PackIndex> loadedIdx = Optionally.empty();
+ private Optionally<PackIndex> loadedIdx = Optionally.empty();
private Optionally<PackReverseIndex> reverseIdx = Optionally.empty();
@@ -159,60 +162,54 @@ public class Pack implements Iterable<PackIndex.MutableEntry> {
length = Long.MAX_VALUE;
}
- private PackIndex idx() throws IOException {
+ private synchronized PackIndex idx() throws IOException {
Optional<PackIndex> optional = loadedIdx.getOptional();
if (optional.isPresent()) {
return optional.get();
}
- synchronized (this) {
- optional = loadedIdx.getOptional();
- if (optional.isPresent()) {
- return optional.get();
- }
- if (invalid) {
- throw new PackInvalidException(packFile, invalidatingCause);
+ if (invalid) {
+ throw new PackInvalidException(packFile, invalidatingCause);
+ }
+ try {
+ long start = System.currentTimeMillis();
+ PackFile idxFile = packFile.create(INDEX);
+ PackIndex idx = PackIndex.open(idxFile);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format(
+ "Opening pack index %s, size %.3f MB took %d ms", //$NON-NLS-1$
+ idxFile.getAbsolutePath(),
+ Float.valueOf(idxFile.length()
+ / (1024f * 1024)),
+ Long.valueOf(System.currentTimeMillis()
+ - start)));
}
- try {
- long start = System.currentTimeMillis();
- PackFile idxFile = packFile.create(INDEX);
- PackIndex idx = PackIndex.open(idxFile);
- if (LOG.isDebugEnabled()) {
- LOG.debug(String.format(
- "Opening pack index %s, size %.3f MB took %d ms", //$NON-NLS-1$
- idxFile.getAbsolutePath(),
- Float.valueOf(idxFile.length()
- / (1024f * 1024)),
- Long.valueOf(System.currentTimeMillis()
- - start)));
- }
-
if (packChecksum == null) {
- packChecksum = idx.packChecksum;
- fileSnapshot.setChecksum(
- ObjectId.fromRaw(packChecksum));
- } else if (!Arrays.equals(packChecksum,
- idx.packChecksum)) {
- throw new PackMismatchException(MessageFormat
- .format(JGitText.get().packChecksumMismatch,
- packFile.getPath(),
- PackExt.PACK.getExtension(),
- Hex.toHexString(packChecksum),
- PackExt.INDEX.getExtension(),
- Hex.toHexString(idx.packChecksum)));
- }
- loadedIdx = optionally(idx);
- return idx;
- } catch (InterruptedIOException e) {
- // don't invalidate the pack, we are interrupted from
- // another thread
- throw e;
- } catch (IOException e) {
- invalid = true;
- invalidatingCause = e;
- throw e;
+ packChecksum = idx.getChecksum();
+ fileSnapshot.setChecksum(
+ ObjectId.fromRaw(packChecksum));
+ } else if (!Arrays.equals(packChecksum,
+ idx.getChecksum())) {
+ throw new PackMismatchException(MessageFormat
+ .format(JGitText.get().packChecksumMismatch,
+ packFile.getPath(),
+ PackExt.PACK.getExtension(),
+ Hex.toHexString(packChecksum),
+ PackExt.INDEX.getExtension(),
+ Hex.toHexString(idx.getChecksum())));
}
+ loadedIdx = optionally(idx);
+ return idx;
+ } catch (InterruptedIOException e) {
+ // don't invalidate the pack, we are interrupted from
+ // another thread
+ throw e;
+ } catch (IOException e) {
+ invalid = true;
+ invalidatingCause = e;
+ throw e;
}
}
+
/**
* Get the File object which locates this pack on disk.
*
@@ -296,15 +293,28 @@ public class Pack implements Iterable<PackIndex.MutableEntry> {
}
/**
- * Close the resources utilized by this repository
+ * Close the resources utilized by these pack files
+ *
+ * @param packs
+ * packs to close
+ */
+ public static void close(Set<Pack> packs) {
+ WindowCache.purge(packs);
+ packs.forEach(p -> p.closeIndices());
+ }
+
+ /**
+ * Close the resources utilized by this pack file
*/
public void close() {
WindowCache.purge(this);
- synchronized (this) {
- loadedIdx.clear();
- reverseIdx.clear();
- bitmapIdx.clear();
- }
+ closeIndices();
+ }
+
+ private synchronized void closeIndices() {
+ loadedIdx.clear();
+ reverseIdx.clear();
+ bitmapIdx.clear();
}
/**
@@ -416,185 +426,202 @@ public class Pack implements Iterable<PackIndex.MutableEntry> {
final CRC32 crc2 = validate ? new CRC32() : null;
final byte[] buf = out.getCopyBuffer();
+ boolean isHeaderWritten = false;
// Rip apart the header so we can discover the size.
//
- readFully(src.offset, buf, 0, 20, curs);
- int c = buf[0] & 0xff;
- final int typeCode = (c >> 4) & 7;
- long inflatedLength = c & 15;
- int shift = 4;
- int headerCnt = 1;
- while ((c & 0x80) != 0) {
- c = buf[headerCnt++] & 0xff;
- inflatedLength += ((long) (c & 0x7f)) << shift;
- shift += 7;
- }
-
- if (typeCode == Constants.OBJ_OFS_DELTA) {
- do {
+ try {
+ readFully(src.offset, buf, 0, 20, curs);
+
+ int c = buf[0] & 0xff;
+ final int typeCode = (c >> 4) & 7;
+ long inflatedLength = c & 15;
+ int shift = 4;
+ int headerCnt = 1;
+ while ((c & 0x80) != 0) {
c = buf[headerCnt++] & 0xff;
- } while ((c & 128) != 0);
- if (validate) {
- assert(crc1 != null && crc2 != null);
- crc1.update(buf, 0, headerCnt);
- crc2.update(buf, 0, headerCnt);
+ inflatedLength += ((long) (c & 0x7f)) << shift;
+ shift += 7;
}
- } else if (typeCode == Constants.OBJ_REF_DELTA) {
- if (validate) {
+
+ if (typeCode == Constants.OBJ_OFS_DELTA) {
+ do {
+ c = buf[headerCnt++] & 0xff;
+ } while ((c & 128) != 0);
+ if (validate) {
+ assert(crc1 != null && crc2 != null);
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+ }
+ } else if (typeCode == Constants.OBJ_REF_DELTA) {
+ if (validate) {
+ assert(crc1 != null && crc2 != null);
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+ }
+
+ readFully(src.offset + headerCnt, buf, 0, 20, curs);
+ if (validate) {
+ assert(crc1 != null && crc2 != null);
+ crc1.update(buf, 0, 20);
+ crc2.update(buf, 0, 20);
+ }
+ headerCnt += 20;
+ } else if (validate) {
assert(crc1 != null && crc2 != null);
crc1.update(buf, 0, headerCnt);
crc2.update(buf, 0, headerCnt);
}
- readFully(src.offset + headerCnt, buf, 0, 20, curs);
- if (validate) {
- assert(crc1 != null && crc2 != null);
- crc1.update(buf, 0, 20);
- crc2.update(buf, 0, 20);
- }
- headerCnt += 20;
- } else if (validate) {
- assert(crc1 != null && crc2 != null);
- crc1.update(buf, 0, headerCnt);
- crc2.update(buf, 0, headerCnt);
- }
-
- final long dataOffset = src.offset + headerCnt;
- final long dataLength = src.length;
- final long expectedCRC;
- final ByteArrayWindow quickCopy;
+ final long dataOffset = src.offset + headerCnt;
+ final long dataLength = src.length;
+ final long expectedCRC;
+ final ByteArrayWindow quickCopy;
- // Verify the object isn't corrupt before sending. If it is,
- // we report it missing instead.
- //
- try {
- quickCopy = curs.quickCopy(this, dataOffset, dataLength);
+ // Verify the object isn't corrupt before sending. If it is,
+ // we report it missing instead.
+ //
+ try {
+ quickCopy = curs.quickCopy(this, dataOffset, dataLength);
- if (validate && idx().hasCRC32Support()) {
- assert(crc1 != null);
- // Index has the CRC32 code cached, validate the object.
- //
- expectedCRC = idx().findCRC32(src);
- if (quickCopy != null) {
- quickCopy.crc32(crc1, dataOffset, (int) dataLength);
- } else {
- long pos = dataOffset;
- long cnt = dataLength;
- while (cnt > 0) {
- final int n = (int) Math.min(cnt, buf.length);
- readFully(pos, buf, 0, n, curs);
- crc1.update(buf, 0, n);
- pos += n;
- cnt -= n;
+ if (validate && idx().hasCRC32Support()) {
+ assert(crc1 != null);
+ // Index has the CRC32 code cached, validate the object.
+ //
+ expectedCRC = idx().findCRC32(src);
+ if (quickCopy != null) {
+ quickCopy.crc32(crc1, dataOffset, (int) dataLength);
+ } else {
+ long pos = dataOffset;
+ long cnt = dataLength;
+ while (cnt > 0) {
+ final int n = (int) Math.min(cnt, buf.length);
+ readFully(pos, buf, 0, n, curs);
+ crc1.update(buf, 0, n);
+ pos += n;
+ cnt -= n;
+ }
}
+ if (crc1.getValue() != expectedCRC) {
+ setCorrupt(src.offset);
+ throw new CorruptObjectException(MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream,
+ Long.valueOf(src.offset), getPackFile()));
+ }
+ } else if (validate) {
+ // We don't have a CRC32 code in the index, so compute it
+ // now while inflating the raw data to get zlib to tell us
+ // whether or not the data is safe.
+ //
+ Inflater inf = curs.inflater();
+ byte[] tmp = new byte[1024];
+ if (quickCopy != null) {
+ quickCopy.check(inf, tmp, dataOffset, (int) dataLength);
+ } else {
+ assert(crc1 != null);
+ long pos = dataOffset;
+ long cnt = dataLength;
+ while (cnt > 0) {
+ final int n = (int) Math.min(cnt, buf.length);
+ readFully(pos, buf, 0, n, curs);
+ crc1.update(buf, 0, n);
+ inf.setInput(buf, 0, n);
+ while (inf.inflate(tmp, 0, tmp.length) > 0)
+ continue;
+ pos += n;
+ cnt -= n;
+ }
+ }
+ if (!inf.finished() || inf.getBytesRead() != dataLength) {
+ setCorrupt(src.offset);
+ throw new EOFException(MessageFormat.format(
+ JGitText.get().shortCompressedStreamAt,
+ Long.valueOf(src.offset)));
+ }
+ assert(crc1 != null);
+ expectedCRC = crc1.getValue();
+ } else {
+ expectedCRC = -1;
}
- if (crc1.getValue() != expectedCRC) {
- setCorrupt(src.offset);
- throw new CorruptObjectException(MessageFormat.format(
- JGitText.get().objectAtHasBadZlibStream,
- Long.valueOf(src.offset), getPackFile()));
- }
- } else if (validate) {
- // We don't have a CRC32 code in the index, so compute it
- // now while inflating the raw data to get zlib to tell us
- // whether or not the data is safe.
+ } catch (DataFormatException dataFormat) {
+ setCorrupt(src.offset);
+
+ CorruptObjectException corruptObject = new CorruptObjectException(
+ MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream,
+ Long.valueOf(src.offset), getPackFile()),
+ dataFormat);
+
+ throw new StoredObjectRepresentationNotAvailableException(
+ corruptObject);
+ }
+
+ if (quickCopy != null) {
+ // The entire object fits into a single byte array window slice,
+ // and we have it pinned. Write this out without copying.
//
- Inflater inf = curs.inflater();
- byte[] tmp = new byte[1024];
- if (quickCopy != null) {
- quickCopy.check(inf, tmp, dataOffset, (int) dataLength);
- } else {
- assert(crc1 != null);
+ out.writeHeader(src, inflatedLength);
+ isHeaderWritten = true;
+ quickCopy.write(out, dataOffset, (int) dataLength);
+
+ } else if (dataLength <= buf.length) {
+ // Tiny optimization: Lots of objects are very small deltas or
+ // deflated commits that are likely to fit in the copy buffer.
+ //
+ if (!validate) {
long pos = dataOffset;
long cnt = dataLength;
while (cnt > 0) {
final int n = (int) Math.min(cnt, buf.length);
readFully(pos, buf, 0, n, curs);
- crc1.update(buf, 0, n);
- inf.setInput(buf, 0, n);
- while (inf.inflate(tmp, 0, tmp.length) > 0)
- continue;
pos += n;
cnt -= n;
}
}
- if (!inf.finished() || inf.getBytesRead() != dataLength) {
- setCorrupt(src.offset);
- throw new EOFException(MessageFormat.format(
- JGitText.get().shortCompressedStreamAt,
- Long.valueOf(src.offset)));
- }
- assert(crc1 != null);
- expectedCRC = crc1.getValue();
+ out.writeHeader(src, inflatedLength);
+ isHeaderWritten = true;
+ out.write(buf, 0, (int) dataLength);
} else {
- expectedCRC = -1;
- }
- } catch (DataFormatException dataFormat) {
- setCorrupt(src.offset);
-
- CorruptObjectException corruptObject = new CorruptObjectException(
- MessageFormat.format(
- JGitText.get().objectAtHasBadZlibStream,
- Long.valueOf(src.offset), getPackFile()),
- dataFormat);
-
- throw new StoredObjectRepresentationNotAvailableException(
- corruptObject);
-
- } catch (IOException ioError) {
- throw new StoredObjectRepresentationNotAvailableException(ioError);
- }
-
- if (quickCopy != null) {
- // The entire object fits into a single byte array window slice,
- // and we have it pinned. Write this out without copying.
- //
- out.writeHeader(src, inflatedLength);
- quickCopy.write(out, dataOffset, (int) dataLength);
-
- } else if (dataLength <= buf.length) {
- // Tiny optimization: Lots of objects are very small deltas or
- // deflated commits that are likely to fit in the copy buffer.
- //
- if (!validate) {
+ // Now we are committed to sending the object. As we spool it out,
+ // check its CRC32 code to make sure there wasn't corruption between
+ // the verification we did above, and us actually outputting it.
+ //
long pos = dataOffset;
long cnt = dataLength;
while (cnt > 0) {
final int n = (int) Math.min(cnt, buf.length);
readFully(pos, buf, 0, n, curs);
- pos += n;
+ if (validate) {
+ assert(crc2 != null);
+ crc2.update(buf, 0, n);
+ }
cnt -= n;
+ if (!isHeaderWritten) {
+ if (invalid && cnt > 0) {
+ // Since this is not the last iteration and the packfile is invalid,
+ // better to assume the iterations will not all complete here while
+ // it is still likely recoverable.
+ throw new StoredObjectRepresentationNotAvailableException(invalidatingCause);
+ }
+ out.writeHeader(src, inflatedLength);
+ isHeaderWritten = true;
+ }
+ out.write(buf, 0, n);
+ pos += n;
}
- }
- out.writeHeader(src, inflatedLength);
- out.write(buf, 0, (int) dataLength);
- } else {
- // Now we are committed to sending the object. As we spool it out,
- // check its CRC32 code to make sure there wasn't corruption between
- // the verification we did above, and us actually outputting it.
- //
- out.writeHeader(src, inflatedLength);
- long pos = dataOffset;
- long cnt = dataLength;
- while (cnt > 0) {
- final int n = (int) Math.min(cnt, buf.length);
- readFully(pos, buf, 0, n, curs);
if (validate) {
assert(crc2 != null);
- crc2.update(buf, 0, n);
+ if (crc2.getValue() != expectedCRC) {
+ throw new CorruptObjectException(MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream,
+ Long.valueOf(src.offset), getPackFile()));
+ }
}
- out.write(buf, 0, n);
- pos += n;
- cnt -= n;
}
- if (validate) {
- assert(crc2 != null);
- if (crc2.getValue() != expectedCRC) {
- throw new CorruptObjectException(MessageFormat.format(
- JGitText.get().objectAtHasBadZlibStream,
- Long.valueOf(src.offset), getPackFile()));
- }
+ } catch (IOException ioError) {
+ if (!isHeaderWritten) {
+ throw new StoredObjectRepresentationNotAvailableException(ioError);
}
+ throw ioError;
}
}
@@ -621,42 +648,53 @@ public class Pack implements Iterable<PackIndex.MutableEntry> {
throw new EOFException();
}
- private synchronized void beginCopyAsIs()
+ private void beginCopyAsIs()
throws StoredObjectRepresentationNotAvailableException {
- if (++activeCopyRawData == 1 && activeWindows == 0) {
- try {
- doOpen();
- } catch (IOException thisPackNotValid) {
- throw new StoredObjectRepresentationNotAvailableException(
- thisPackNotValid);
+ synchronized (activeLock) {
+ if (++activeCopyRawData == 1 && activeWindows == 0) {
+ try {
+ doOpen();
+ } catch (IOException thisPackNotValid) {
+ throw new StoredObjectRepresentationNotAvailableException(
+ thisPackNotValid);
+ }
}
}
}
- private synchronized void endCopyAsIs() {
- if (--activeCopyRawData == 0 && activeWindows == 0)
- doClose();
+ private void endCopyAsIs() {
+ synchronized (activeLock) {
+ if (--activeCopyRawData == 0 && activeWindows == 0) {
+ doClose();
+ }
+ }
}
- synchronized boolean beginWindowCache() throws IOException {
- if (++activeWindows == 1) {
- if (activeCopyRawData == 0)
- doOpen();
- return true;
+ boolean beginWindowCache() throws IOException {
+ synchronized (activeLock) {
+ if (++activeWindows == 1) {
+ if (activeCopyRawData == 0) {
+ doOpen();
+ }
+ return true;
+ }
+ return false;
}
- return false;
}
- synchronized boolean endWindowCache() {
- final boolean r = --activeWindows == 0;
- if (r && activeCopyRawData == 0)
- doClose();
- return r;
+ boolean endWindowCache() {
+ synchronized (activeLock) {
+ boolean r = --activeWindows == 0;
+ if (r && activeCopyRawData == 0) {
+ doClose();
+ }
+ return r;
+ }
}
private void doOpen() throws IOException {
if (invalid) {
- openFail(true, invalidatingCause);
+ openFail(invalidatingCause);
throw new PackInvalidException(packFile, invalidatingCause);
}
try {
@@ -667,39 +705,41 @@ public class Pack implements Iterable<PackIndex.MutableEntry> {
}
} catch (InterruptedIOException e) {
// don't invalidate the pack, we are interrupted from another thread
- openFail(false, e);
+ openFail(e);
throw e;
} catch (FileNotFoundException fn) {
- // don't invalidate the pack if opening an existing file failed
- // since it may be related to a temporary lack of resources (e.g.
- // max open files)
- openFail(!packFile.exists(), fn);
+ if (!packFile.exists()) {
+ // Failure to open an existing file may be related to a temporary lack of resources
+ // (e.g. max open files)
+ invalid = true;
+ }
+ openFail(fn);
throw fn;
} catch (EOFException | AccessDeniedException | NoSuchFileException
| CorruptObjectException | NoPackSignatureException
| PackMismatchException | UnpackException
| UnsupportedPackIndexVersionException
| UnsupportedPackVersionException pe) {
- // exceptions signaling permanent problems with a pack
- openFail(true, pe);
+ invalid = true; // exceptions signaling permanent problems with a pack
+ openFail(pe);
throw pe;
} catch (IOException ioe) {
- // mark this packfile as invalid when NFS stale file handle error
- // occur
- openFail(FileUtils.isStaleFileHandleInCausalChain(ioe), ioe);
+ if (FileUtils.isStaleFileHandleInCausalChain(ioe)) {
+ invalid = true;
+ }
+ openFail(ioe);
throw ioe;
} catch (RuntimeException ge) {
// generic exceptions could be transient so we should not mark the
// pack invalid to avoid false MissingObjectExceptions
- openFail(false, ge);
+ openFail(ge);
throw ge;
}
}
- private void openFail(boolean invalidate, Exception cause) {
+ private void openFail(Exception cause) {
activeWindows = 0;
activeCopyRawData = 0;
- invalid = invalidate;
invalidatingCause = cause;
doClose();
}
@@ -791,7 +831,7 @@ public class Pack implements Iterable<PackIndex.MutableEntry> {
MessageFormat.format(JGitText.get().packChecksumMismatch,
getPackFile(), PackExt.PACK.getExtension(),
Hex.toHexString(buf), PackExt.INDEX.getExtension(),
- Hex.toHexString(idx.packChecksum)));
+ Hex.toHexString(idx.getChecksum())));
}
}
@@ -1173,17 +1213,8 @@ public class Pack implements Iterable<PackIndex.MutableEntry> {
return null;
}
- synchronized void refreshBitmapIndex(PackFile bitmapIndexFile) {
- this.bitmapIdx = Optionally.empty();
- this.invalid = false;
+ void setBitmapIndexFile(PackFile bitmapIndexFile) {
this.bitmapIdxFile = bitmapIndexFile;
- try {
- getBitmapIndex();
- } catch (IOException e) {
- LOG.warn(JGitText.get().bitmapFailedToGet, bitmapIdxFile, e);
- this.bitmapIdx = Optionally.empty();
- this.bitmapIdxFile = null;
- }
}
private synchronized PackReverseIndex getReverseIdx() throws IOException {
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
index 8221cff442..f50c17eafa 100644
--- 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
@@ -17,6 +17,8 @@ import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -24,6 +26,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -41,7 +44,8 @@ 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.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.TrustStat;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.util.FileUtils;
@@ -71,7 +75,7 @@ class PackDirectory {
private final AtomicReference<PackList> packList;
- private final boolean trustFolderStat;
+ private final TrustStat trustPackStat;
/**
* Initialize a reference to an on-disk 'pack' directory.
@@ -85,14 +89,7 @@ class PackDirectory {
this.config = config;
this.directory = directory;
packList = new AtomicReference<>(NO_PACKS);
-
- // 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 folder's size,
- // modification time, and key (inode) and assume that no new pack files
- // can be in this folder if these attributes have not changed.
- trustFolderStat = config.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
- ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
+ trustPackStat = config.get(CoreConfig.KEY).getTrustPackStat();
}
/**
@@ -111,9 +108,7 @@ class PackDirectory {
void close() {
PackList packs = packList.get();
if (packs != NO_PACKS && packList.compareAndSet(packs, NO_PACKS)) {
- for (Pack p : packs.packs) {
- p.close();
- }
+ Pack.close(Set.of(packs.packs));
}
}
@@ -314,38 +309,42 @@ class PackDirectory {
}
private void handlePackError(IOException e, Pack p) {
- String warnTmpl = null;
+ String warnTemplate = null;
+ String debugTemplate = null;
int transientErrorCount = 0;
- String errTmpl = JGitText.get().exceptionWhileReadingPack;
+ String errorTemplate = JGitText.get().exceptionWhileReadingPack;
if ((e instanceof CorruptObjectException)
|| (e instanceof PackInvalidException)) {
- warnTmpl = JGitText.get().corruptPack;
- LOG.warn(MessageFormat.format(warnTmpl,
+ warnTemplate = JGitText.get().corruptPack;
+ LOG.warn(MessageFormat.format(warnTemplate,
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;
+ errorTemplate = JGitText.get().packInaccessible;
transientErrorCount = p.incrementTransientErrorCount();
} else {
- warnTmpl = JGitText.get().packWasDeleted;
+ debugTemplate = JGitText.get().packWasDeleted;
remove(p);
}
} else if (FileUtils.isStaleFileHandleInCausalChain(e)) {
- warnTmpl = JGitText.get().packHandleIsStale;
+ warnTemplate = JGitText.get().packHandleIsStale;
remove(p);
} else {
transientErrorCount = p.incrementTransientErrorCount();
}
- if (warnTmpl != null) {
- LOG.warn(MessageFormat.format(warnTmpl,
+ if (warnTemplate != null) {
+ LOG.warn(MessageFormat.format(warnTemplate,
p.getPackFile().getAbsolutePath()), e);
+ } else if (debugTemplate != null) {
+ LOG.debug(MessageFormat.format(debugTemplate,
+ 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,
+ LOG.error(MessageFormat.format(errorTemplate,
p.getPackFile().getAbsolutePath(),
Integer.valueOf(transientErrorCount)), e);
}
@@ -362,8 +361,26 @@ class PackDirectory {
}
boolean searchPacksAgain(PackList old) {
- return (!trustFolderStat || old.snapshot.isModified(directory))
- && old != scanPacks(old);
+ switch (trustPackStat) {
+ case NEVER:
+ break;
+ case AFTER_OPEN:
+ try (InputStream stream = Files
+ .newInputStream(directory.toPath())) {
+ // open the pack directory to refresh attributes (on some NFS clients)
+ } catch (IOException e) {
+ // ignore
+ }
+ //$FALL-THROUGH$
+ case ALWAYS:
+ if (!old.snapshot.isModified(directory)) {
+ return false;
+ }
+ break;
+ case INHERIT:
+ // only used in CoreConfig internally
+ }
+ return old != scanPacks(old);
}
void insert(Pack pack) {
@@ -460,12 +477,9 @@ class PackDirectory {
&& !oldPack.getFileSnapshot().isModified(packFile)) {
forReuse.remove(packFile.getName());
list.add(oldPack);
- try {
- if(oldPack.getBitmapIndex() == null) {
- oldPack.refreshBitmapIndex(packFilesByExt.get(BITMAP_INDEX));
- }
- } catch (IOException e) {
- LOG.warn(JGitText.get().bitmapAccessErrorForPackfile, oldPack.getPackName(), e);
+ PackFile bitMaps = packFilesByExt.get(BITMAP_INDEX);
+ if (bitMaps != null) {
+ oldPack.setBitmapIndexFile(bitMaps);
}
continue;
}
@@ -484,9 +498,7 @@ class PackDirectory {
return old;
}
- for (Pack p : forReuse.values()) {
- p.close();
- }
+ Pack.close(new HashSet<>(forReuse.values()));
if (list.isEmpty()) {
return new PackList(snapshot, NO_PACKS.packs);
@@ -545,7 +557,7 @@ class PackDirectory {
for (String name : nameList) {
try {
PackFile pack = new PackFile(directory, name);
- if (pack.getPackExt() != null) {
+ if (pack.getPackExt() != null && !pack.isTmpGCFile()) {
Map<PackExt, PackFile> packByExt = packFilesByExtById
.get(pack.getId());
if (packByExt == null) {
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/PackFile.java
index 19979d0ed5..c9b05ad025 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/PackFile.java
@@ -27,6 +27,7 @@ public class PackFile extends File {
private static final long serialVersionUID = 1L;
private static final String PREFIX = "pack-"; //$NON-NLS-1$
+ private static final String TMP_GC_PREFIX = ".tmp-"; //$NON-NLS-1$
private final String base; // PREFIX + id i.e.
// pack-0123456789012345678901234567890123456789
@@ -126,6 +127,13 @@ public class PackFile extends File {
}
/**
+ * @return whether the file is a temporary GC file
+ */
+ public boolean isTmpGCFile() {
+ return id.startsWith(TMP_GC_PREFIX);
+ }
+
+ /**
* Create a new similar PackFile with the given extension instead.
*
* @param ext
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 c2c3775d67..b3e4efb4fc 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
@@ -42,8 +42,8 @@ import org.eclipse.jgit.util.io.SilentFileInputStream;
* by ObjectId.
* </p>
*/
-public abstract class PackIndex
- implements Iterable<PackIndex.MutableEntry>, ObjectIdSet {
+public interface PackIndex
+ extends Iterable<PackIndex.MutableEntry>, ObjectIdSet {
/**
* Open an existing pack <code>.idx</code> file for reading.
* <p>
@@ -61,7 +61,7 @@ public abstract class PackIndex
* the file exists but could not be read due to security errors,
* unrecognized data version, or unexpected data corruption.
*/
- public static PackIndex open(File idxFile) throws IOException {
+ static PackIndex open(File idxFile) throws IOException {
try (SilentFileInputStream fd = new SilentFileInputStream(
idxFile)) {
return read(fd);
@@ -92,7 +92,7 @@ public abstract class PackIndex
* @throws org.eclipse.jgit.errors.CorruptObjectException
* the stream does not contain a valid pack index.
*/
- public static PackIndex read(InputStream fd) throws IOException,
+ static PackIndex read(InputStream fd) throws IOException,
CorruptObjectException {
final byte[] hdr = new byte[8];
IO.readFully(fd, hdr, 0, hdr.length);
@@ -109,16 +109,13 @@ public abstract class PackIndex
}
private static boolean isTOC(byte[] h) {
- final byte[] toc = PackIndexWriter.TOC;
+ final byte[] toc = BasePackIndexWriter.TOC;
for (int i = 0; i < toc.length; i++)
if (h[i] != toc[i])
return false;
return true;
}
- /** Footer checksum applied on the bottom of the pack file. */
- protected byte[] packChecksum;
-
/**
* Determine if an object is contained within the pack file.
*
@@ -126,12 +123,12 @@ public abstract class PackIndex
* the object to look for. Must not be null.
* @return true if the object is listed in this index; false otherwise.
*/
- public boolean hasObject(AnyObjectId id) {
+ default boolean hasObject(AnyObjectId id) {
return findOffset(id) != -1;
}
@Override
- public boolean contains(AnyObjectId id) {
+ default boolean contains(AnyObjectId id) {
return findOffset(id) != -1;
}
@@ -147,7 +144,7 @@ public abstract class PackIndex
* </p>
*/
@Override
- public abstract Iterator<MutableEntry> iterator();
+ Iterator<MutableEntry> iterator();
/**
* Obtain the total number of objects described by this index.
@@ -155,7 +152,7 @@ public abstract class PackIndex
* @return number of objects in this index, and likewise in the associated
* pack that this index was generated from.
*/
- public abstract long getObjectCount();
+ long getObjectCount();
/**
* Obtain the total number of objects needing 64 bit offsets.
@@ -163,7 +160,7 @@ public abstract class PackIndex
* @return number of objects in this index using a 64 bit offset; that is an
* object positioned after the 2 GB position within the file.
*/
- public abstract long getOffset64Count();
+ long getOffset64Count();
/**
* Get ObjectId for the n-th object entry returned by {@link #iterator()}.
@@ -185,7 +182,7 @@ public abstract class PackIndex
* is 0, the second is 1, etc.
* @return the ObjectId for the corresponding entry.
*/
- public abstract ObjectId getObjectId(long nthPosition);
+ ObjectId getObjectId(long nthPosition);
/**
* Get ObjectId for the n-th object entry returned by {@link #iterator()}.
@@ -209,7 +206,7 @@ public abstract class PackIndex
* negative, but still valid.
* @return the ObjectId for the corresponding entry.
*/
- public final ObjectId getObjectId(int nthPosition) {
+ default ObjectId getObjectId(int nthPosition) {
if (nthPosition >= 0)
return getObjectId((long) nthPosition);
final int u31 = nthPosition >>> 1;
@@ -228,7 +225,7 @@ public abstract class PackIndex
* etc. Positions past 2**31-1 are negative, but still valid.
* @return the offset in a pack for the corresponding entry.
*/
- protected abstract long getOffset(long nthPosition);
+ long getOffset(long nthPosition);
/**
* Locate the file offset position for the requested object.
@@ -239,7 +236,7 @@ public abstract class PackIndex
* object does not exist in this index and is thus not stored in the
* associated pack.
*/
- public abstract long findOffset(AnyObjectId objId);
+ long findOffset(AnyObjectId objId);
/**
* Locate the position of this id in the list of object-ids in the index
@@ -250,7 +247,7 @@ public abstract class PackIndex
* of ids stored in this index; -1 if the object does not exist in
* this index and is thus not stored in the associated pack.
*/
- public abstract int findPosition(AnyObjectId objId);
+ int findPosition(AnyObjectId objId);
/**
* Retrieve stored CRC32 checksum of the requested object raw-data
@@ -264,7 +261,7 @@ public abstract class PackIndex
* @throws java.lang.UnsupportedOperationException
* when this index doesn't support CRC32 checksum
*/
- public abstract long findCRC32(AnyObjectId objId)
+ long findCRC32(AnyObjectId objId)
throws MissingObjectException, UnsupportedOperationException;
/**
@@ -272,7 +269,7 @@ public abstract class PackIndex
*
* @return true if CRC32 is stored, false otherwise
*/
- public abstract boolean hasCRC32Support();
+ boolean hasCRC32Support();
/**
* Find objects matching the prefix abbreviation.
@@ -288,8 +285,8 @@ public abstract class PackIndex
* @throws java.io.IOException
* the index cannot be read.
*/
- public abstract void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
- int matchLimit) throws IOException;
+ void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+ int matchLimit) throws IOException;
/**
* Get pack checksum
@@ -297,18 +294,18 @@ public abstract class PackIndex
* @return the checksum of the pack; caller must not modify it
* @since 5.5
*/
- public byte[] getChecksum() {
- return packChecksum;
- }
+ byte[] getChecksum();
/**
* Represent mutable entry of pack index consisting of object id and offset
* in pack (both mutable).
*
*/
- public static class MutableEntry {
+ class MutableEntry {
+ /** Buffer of the ObjectId visited by the EntriesIterator. */
final MutableObjectId idBuffer = new MutableObjectId();
+ /** Offset into the packfile of the current object. */
long offset;
/**
@@ -326,7 +323,6 @@ public abstract class PackIndex
* @return hex string describing the object id of this entry.
*/
public String name() {
- ensureId();
return idBuffer.name();
}
@@ -336,7 +332,6 @@ public abstract class PackIndex
* @return a copy of the object id.
*/
public ObjectId toObjectId() {
- ensureId();
return idBuffer.toObjectId();
}
@@ -347,27 +342,64 @@ public abstract class PackIndex
*/
public MutableEntry cloneEntry() {
final MutableEntry r = new MutableEntry();
- ensureId();
r.idBuffer.fromObjectId(idBuffer);
r.offset = offset;
return r;
}
- void ensureId() {
- // Override in implementations.
+ /**
+ * Similar to {@link Comparable#compareTo(Object)}, using only the
+ * object id in the entry.
+ *
+ * @param other
+ * Another mutable entry (probably from another index)
+ *
+ * @return a negative integer, zero, or a positive integer as this
+ * object is less than, equal to, or greater than the specified
+ * object.
+ */
+ public int compareBySha1To(MutableEntry other) {
+ return idBuffer.compareTo(other.idBuffer);
+ }
+
+ /**
+ * Copy the current ObjectId to dest
+ * <p>
+ * Like {@link #toObjectId()}, but reusing the destination instead of
+ * creating a new ObjectId instance.
+ *
+ * @param dest
+ * destination for the object id
+ */
+ public void copyOidTo(MutableObjectId dest) {
+ dest.fromObjectId(idBuffer);
}
}
+ /**
+ * Base implementation of the iterator over index entries.
+ */
abstract class EntriesIterator implements Iterator<MutableEntry> {
- protected final MutableEntry entry = initEntry();
+ private final long objectCount;
- protected long returnedNumber = 0;
+ private final MutableEntry entry = new MutableEntry();
- protected abstract MutableEntry initEntry();
+ /** Counts number of entries accessed so far. */
+ private long returnedNumber = 0;
+
+ /**
+ * Construct an iterator that can move objectCount times forward.
+ *
+ * @param objectCount
+ * the number of objects in the PackFile.
+ */
+ protected EntriesIterator(long objectCount) {
+ this.objectCount = objectCount;
+ }
@Override
public boolean hasNext() {
- return returnedNumber < getObjectCount();
+ return returnedNumber < objectCount;
}
/**
@@ -375,7 +407,55 @@ public abstract class PackIndex
* element.
*/
@Override
- public abstract MutableEntry next();
+ public MutableEntry next() {
+ readNext();
+ returnedNumber++;
+ return entry;
+ }
+
+ /**
+ * Used by subclasses to load the next entry into the MutableEntry.
+ * <p>
+ * Subclasses are expected to populate the entry with
+ * {@link #setIdBuffer} and {@link #setOffset}.
+ */
+ protected abstract void readNext();
+
+ /**
+ * Copies to the entry an {@link ObjectId} from the int buffer and
+ * position idx
+ *
+ * @param raw
+ * the raw data
+ * @param idx
+ * the index into {@code raw}
+ */
+ protected void setIdBuffer(int[] raw, int idx) {
+ entry.idBuffer.fromRaw(raw, idx);
+ }
+
+ /**
+ * Copies to the entry an {@link ObjectId} from the byte array at
+ * position idx.
+ *
+ * @param raw
+ * the raw data
+ * @param idx
+ * the index into {@code raw}
+ */
+ protected void setIdBuffer(byte[] raw, int idx) {
+ entry.idBuffer.fromRaw(raw, idx);
+ }
+
+ /**
+ * Sets the {@code offset} to the entry
+ *
+ * @param offset
+ * the offset in the pack file
+ */
+ protected void setOffset(long offset) {
+ entry.offset = offset;
+ }
@Override
public void remove() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
index 5180df46bf..be48358a0d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
@@ -29,13 +29,16 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.NB;
-class PackIndexV1 extends PackIndex {
+class PackIndexV1 implements PackIndex {
private static final int IDX_HDR_LEN = 256 * 4;
private static final int RECORD_SIZE = 4 + Constants.OBJECT_ID_LENGTH;
private final long[] idxHeader;
+ /** Footer checksum applied on the bottom of the pack file. */
+ protected byte[] packChecksum;
+
byte[][] idxdata;
private long objectCnt;
@@ -118,7 +121,7 @@ class PackIndexV1 extends PackIndex {
}
@Override
- protected long getOffset(long nthPosition) {
+ public long getOffset(long nthPosition) {
final int levelOne = findLevelOne(nthPosition);
final int levelTwo = getLevelTwo(nthPosition, levelOne);
final int p = (4 + Constants.OBJECT_ID_LENGTH) * levelTwo;
@@ -200,7 +203,7 @@ class PackIndexV1 extends PackIndex {
@Override
public Iterator<MutableEntry> iterator() {
- return new IndexV1Iterator();
+ return new EntriesIteratorV1(this);
}
@Override
@@ -238,32 +241,35 @@ class PackIndexV1 extends PackIndex {
return (RECORD_SIZE * mid) + 4;
}
- private class IndexV1Iterator extends EntriesIterator {
- int levelOne;
+ @Override
+ public byte[] getChecksum() {
+ return packChecksum;
+ }
- int levelTwo;
+ private static class EntriesIteratorV1 extends EntriesIterator {
+ private int levelOne;
- @Override
- protected MutableEntry initEntry() {
- return new MutableEntry() {
- @Override
- protected void ensureId() {
- idBuffer.fromRaw(idxdata[levelOne], levelTwo
- - Constants.OBJECT_ID_LENGTH);
- }
- };
+ private int levelTwo;
+
+ private final PackIndexV1 packIndex;
+
+ private EntriesIteratorV1(PackIndexV1 packIndex) {
+ super(packIndex.objectCnt);
+ this.packIndex = packIndex;
}
@Override
- public MutableEntry next() {
- for (; levelOne < idxdata.length; levelOne++) {
- if (idxdata[levelOne] == null)
+ protected void readNext() {
+ for (; levelOne < packIndex.idxdata.length; levelOne++) {
+ if (packIndex.idxdata[levelOne] == null)
continue;
- if (levelTwo < idxdata[levelOne].length) {
- entry.offset = NB.decodeUInt32(idxdata[levelOne], levelTwo);
- levelTwo += Constants.OBJECT_ID_LENGTH + 4;
- returnedNumber++;
- return entry;
+ if (levelTwo < packIndex.idxdata[levelOne].length) {
+ super.setOffset(NB.decodeUInt32(packIndex.idxdata[levelOne],
+ levelTwo));
+ this.levelTwo += Constants.OBJECT_ID_LENGTH + 4;
+ super.setIdBuffer(packIndex.idxdata[levelOne],
+ levelTwo - Constants.OBJECT_ID_LENGTH);
+ return;
}
levelTwo = 0;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
index 751b62dc40..36e54fcd62 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
@@ -28,7 +28,7 @@ import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.NB;
/** Support for the pack index v2 format. */
-class PackIndexV2 extends PackIndex {
+class PackIndexV2 implements PackIndex {
private static final long IS_O64 = 1L << 31;
private static final int FANOUT = 256;
@@ -37,6 +37,9 @@ class PackIndexV2 extends PackIndex {
private static final byte[] NO_BYTES = {};
+ /** Footer checksum applied on the bottom of the pack file. */
+ protected byte[] packChecksum;
+
private long objectCnt;
private final long[] fanoutTable;
@@ -221,7 +224,7 @@ class PackIndexV2 extends PackIndex {
@Override
public Iterator<MutableEntry> iterator() {
- return new EntriesIteratorV2();
+ return new EntriesIteratorV2(this);
}
@Override
@@ -281,37 +284,39 @@ class PackIndexV2 extends PackIndex {
return -1;
}
- private class EntriesIteratorV2 extends EntriesIterator {
- int levelOne;
+ @Override
+ public byte[] getChecksum() {
+ return packChecksum;
+ }
- int levelTwo;
+ private static class EntriesIteratorV2 extends EntriesIterator {
+ private int levelOne = 0;
- @Override
- protected MutableEntry initEntry() {
- return new MutableEntry() {
- @Override
- protected void ensureId() {
- idBuffer.fromRaw(names[levelOne], levelTwo
- - Constants.OBJECT_ID_LENGTH / 4);
- }
- };
+ private int levelTwo = 0;
+
+ private final PackIndexV2 packIndex;
+
+ private EntriesIteratorV2(PackIndexV2 packIndex) {
+ super(packIndex.objectCnt);
+ this.packIndex = packIndex;
}
@Override
- public MutableEntry next() {
- for (; levelOne < names.length; levelOne++) {
- if (levelTwo < names[levelOne].length) {
+ protected void readNext() {
+ for (; levelOne < packIndex.names.length; levelOne++) {
+ if (levelTwo < packIndex.names[levelOne].length) {
int idx = levelTwo / (Constants.OBJECT_ID_LENGTH / 4) * 4;
- long offset = NB.decodeUInt32(offset32[levelOne], idx);
+ long offset = NB.decodeUInt32(packIndex.offset32[levelOne],
+ idx);
if ((offset & IS_O64) != 0) {
idx = (8 * (int) (offset & ~IS_O64));
- offset = NB.decodeUInt64(offset64, idx);
+ offset = NB.decodeUInt64(packIndex.offset64, idx);
}
- entry.offset = offset;
-
- levelTwo += Constants.OBJECT_ID_LENGTH / 4;
- returnedNumber++;
- return entry;
+ super.setOffset(offset);
+ this.levelTwo += Constants.OBJECT_ID_LENGTH / 4;
+ super.setIdBuffer(packIndex.names[levelOne],
+ levelTwo - Constants.OBJECT_ID_LENGTH / 4);
+ return;
}
levelTwo = 0;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV1.java
index 7e28b5eb2b..f0b6193066 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV1.java
@@ -21,10 +21,10 @@ import org.eclipse.jgit.util.NB;
/**
* Creates the version 1 (old style) pack table of contents files.
*
- * @see PackIndexWriter
+ * @see BasePackIndexWriter
* @see PackIndexV1
*/
-class PackIndexWriterV1 extends PackIndexWriter {
+class PackIndexWriterV1 extends BasePackIndexWriter {
static boolean canStore(PackedObjectInfo oe) {
// We are limited to 4 GB per pack as offset is 32 bit unsigned int.
//
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV2.java
index fc5ef61912..b72b35a464 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV2.java
@@ -19,10 +19,10 @@ import org.eclipse.jgit.util.NB;
/**
* Creates the version 2 pack table of contents files.
*
- * @see PackIndexWriter
+ * @see BasePackIndexWriter
* @see PackIndexV2
*/
-class PackIndexWriterV2 extends PackIndexWriter {
+class PackIndexWriterV2 extends BasePackIndexWriter {
private static final int MAX_OFFSET_32 = 0x7fffffff;
private static final int IS_OFFSET_64 = 0x80000000;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInserter.java
index 1b092a3332..55e047bd43 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInserter.java
@@ -77,6 +77,7 @@ import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackIndexWriter;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
@@ -320,7 +321,8 @@ public class PackInserter extends ObjectInserter {
private static void writePackIndex(File idx, byte[] packHash,
List<PackedObjectInfo> list) throws IOException {
try (OutputStream os = new FileOutputStream(idx)) {
- PackIndexWriter w = PackIndexWriter.createVersion(os, INDEX_VERSION);
+ PackIndexWriter w = BasePackIndexWriter.createVersion(os,
+ INDEX_VERSION);
w.write(list, packHash);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java
index a3d74be040..9957f54fbf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java
@@ -12,7 +12,7 @@ package org.eclipse.jgit.internal.storage.file;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
-import java.util.Arrays;
+import java.text.MessageFormat;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.util.NB;
@@ -35,7 +35,7 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex {
private final UInt24Array positions24;
- private final int[] positions32;
+ private final IntArray positions32;
/**
* Parallel array to concat(positions24, positions32) with the size of the
@@ -45,35 +45,37 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex {
* doesn't fit in an int and |value|-1 is the position for the size in the
* size64 array e.g. a value of -1 is sizes64[0], -2 = sizes64[1], ...
*/
- private final int[] sizes32;
+ private final IntArray sizes32;
- private final long[] sizes64;
+ private final LongArray sizes64;
static PackObjectSizeIndex parse(InputStream in) throws IOException {
/** Header and version already out of the input */
- IndexInputStreamReader stream = new IndexInputStreamReader(in);
- int threshold = stream.readInt(); // minSize
- int objCount = stream.readInt();
+ byte[] buffer = new byte[8];
+ in.readNBytes(buffer, 0, 8);
+ int threshold = NB.decodeInt32(buffer, 0); // minSize
+ int objCount = NB.decodeInt32(buffer, 4);
if (objCount == 0) {
return new EmptyPackObjectSizeIndex(threshold);
}
- return new PackObjectSizeIndexV1(stream, threshold, objCount);
+ return new PackObjectSizeIndexV1(in, threshold, objCount);
}
- private PackObjectSizeIndexV1(IndexInputStreamReader stream, int threshold,
+ private PackObjectSizeIndexV1(InputStream stream, int threshold,
int objCount) throws IOException {
this.threshold = threshold;
UInt24Array pos24 = null;
- int[] pos32 = null;
+ IntArray pos32 = null;
+ StreamHelper helper = new StreamHelper();
byte positionEncoding;
- while ((positionEncoding = stream.readByte()) != 0) {
+ while ((positionEncoding = helper.readByte(stream)) != 0) {
if (Byte.compareUnsigned(positionEncoding, BITS_24) == 0) {
- int sz = stream.readInt();
+ int sz = helper.readInt(stream);
pos24 = new UInt24Array(stream.readNBytes(sz * 3));
} else if (Byte.compareUnsigned(positionEncoding, BITS_32) == 0) {
- int sz = stream.readInt();
- pos32 = stream.readIntArray(sz);
+ int sz = helper.readInt(stream);
+ pos32 = IntArray.from(stream, sz);
} else {
throw new UnsupportedEncodingException(
String.format(JGitText.get().unknownPositionEncoding,
@@ -81,16 +83,16 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex {
}
}
positions24 = pos24 != null ? pos24 : UInt24Array.EMPTY;
- positions32 = pos32 != null ? pos32 : new int[0];
+ positions32 = pos32 != null ? pos32 : IntArray.EMPTY;
- sizes32 = stream.readIntArray(objCount);
- int c64sizes = stream.readInt();
+ sizes32 = IntArray.from(stream, objCount);
+ int c64sizes = helper.readInt(stream);
if (c64sizes == 0) {
- sizes64 = new long[0];
+ sizes64 = LongArray.EMPTY;
return;
}
- sizes64 = stream.readLongArray(c64sizes);
- int c128sizes = stream.readInt();
+ sizes64 = LongArray.from(stream, c64sizes);
+ int c128sizes = helper.readInt(stream);
if (c128sizes != 0) {
// this MUST be 0 (we don't support 128 bits sizes yet)
throw new IOException(JGitText.get().unsupportedSizesObjSizeIndex);
@@ -102,8 +104,8 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex {
int pos = -1;
if (!positions24.isEmpty() && idxOffset <= positions24.getLastValue()) {
pos = positions24.binarySearch(idxOffset);
- } else if (positions32.length > 0 && idxOffset >= positions32[0]) {
- int pos32 = Arrays.binarySearch(positions32, idxOffset);
+ } else if (!positions32.empty() && idxOffset >= positions32.get(0)) {
+ int pos32 = positions32.binarySearch(idxOffset);
if (pos32 >= 0) {
pos = pos32 + positions24.size();
}
@@ -112,17 +114,17 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex {
return -1;
}
- int objSize = sizes32[pos];
+ int objSize = sizes32.get(pos);
if (objSize < 0) {
int secondPos = Math.abs(objSize) - 1;
- return sizes64[secondPos];
+ return sizes64.get(secondPos);
}
return objSize;
}
@Override
public long getObjectCount() {
- return (long) positions24.size() + positions32.length;
+ return (long) positions24.size() + positions32.size();
}
@Override
@@ -131,69 +133,128 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex {
}
/**
- * Wrapper to read parsed content from the byte stream
+ * A byte[] that should be interpreted as an int[]
*/
- private static class IndexInputStreamReader {
+ private static class IntArray {
+ private static final IntArray EMPTY = new IntArray(new byte[0]);
- private final byte[] buffer = new byte[8];
+ private static final int INT_SIZE = 4;
- private final InputStream in;
+ private final byte[] data;
- IndexInputStreamReader(InputStream in) {
- this.in = in;
- }
+ private final int size;
- int readInt() throws IOException {
- int n = in.readNBytes(buffer, 0, 4);
- if (n < 4) {
- throw new IOException(JGitText.get().unableToReadFullInt);
+ static IntArray from(InputStream in, int ints) throws IOException {
+ int expectedBytes = ints * INT_SIZE;
+ byte[] data = in.readNBytes(expectedBytes);
+ if (data.length < expectedBytes) {
+ throw new IOException(MessageFormat
+ .format(JGitText.get().unableToReadFullArray,
+ Integer.valueOf(ints)));
}
- return NB.decodeInt32(buffer, 0);
+ return new IntArray(data);
+ }
+
+ private IntArray(byte[] data) {
+ this.data = data;
+ size = data.length / INT_SIZE;
}
- int[] readIntArray(int intsCount) throws IOException {
- if (intsCount == 0) {
- return new int[0];
+ /**
+ * Returns position of element in array, -1 if not there
+ *
+ * @param needle
+ * element to look for
+ * @return position of the element in the array or -1 if not found
+ */
+ int binarySearch(int needle) {
+ if (size == 0) {
+ return -1;
}
+ int high = size;
+ int low = 0;
+ do {
+ int mid = (low + high) >>> 1;
+ int cmp = Integer.compare(needle, get(mid));
+ if (cmp < 0)
+ high = mid;
+ else if (cmp == 0) {
+ return mid;
+ } else
+ low = mid + 1;
+ } while (low < high);
+ return -1;
+ }
- int[] dest = new int[intsCount];
- for (int i = 0; i < intsCount; i++) {
- dest[i] = readInt();
+ int get(int position) {
+ if (position < 0 || position >= size) {
+ throw new IndexOutOfBoundsException(position);
}
- return dest;
+ return NB.decodeInt32(data, position * INT_SIZE);
}
- long readLong() throws IOException {
- int n = in.readNBytes(buffer, 0, 8);
- if (n < 8) {
- throw new IOException(JGitText.get().unableToReadFullInt);
+ boolean empty() {
+ return size == 0;
+ }
+
+ int size() {
+ return size;
+ }
+ }
+
+ /**
+ * A byte[] that should be interpreted as an long[]
+ */
+ private static class LongArray {
+ private static final LongArray EMPTY = new LongArray(new byte[0]);
+
+ private static final int LONG_SIZE = 8; // bytes
+
+ private final byte[] data;
+
+ private final int size;
+
+ static LongArray from(InputStream in, int longs) throws IOException {
+ byte[] data = in.readNBytes(longs * LONG_SIZE);
+ if (data.length < longs * LONG_SIZE) {
+ throw new IOException(MessageFormat
+ .format(JGitText.get().unableToReadFullArray,
+ Integer.valueOf(longs)));
}
- return NB.decodeInt64(buffer, 0);
+ return new LongArray(data);
}
- long[] readLongArray(int longsCount) throws IOException {
- if (longsCount == 0) {
- return new long[0];
+ private LongArray(byte[] data) {
+ this.data = data;
+ size = data.length / LONG_SIZE;
+ }
+
+ long get(int position) {
+ if (position < 0 || position >= size) {
+ throw new IndexOutOfBoundsException(position);
}
+ return NB.decodeInt64(data, position * LONG_SIZE);
+ }
+ }
- long[] dest = new long[longsCount];
- for (int i = 0; i < longsCount; i++) {
- dest[i] = readLong();
+ private static class StreamHelper {
+ private final byte[] buffer = new byte[8];
+
+ int readInt(InputStream in) throws IOException {
+ int n = in.readNBytes(buffer, 0, 4);
+ if (n < 4) {
+ throw new IOException(JGitText.get().unableToReadFullInt);
}
- return dest;
+ return NB.decodeInt32(buffer, 0);
}
- byte readByte() throws IOException {
+ byte readByte(InputStream in) throws IOException {
int n = in.readNBytes(buffer, 0, 1);
if (n != 1) {
throw new IOException(JGitText.get().cannotReadByte);
}
return buffer[0];
}
-
- byte[] readNBytes(int sz) throws IOException {
- return in.readNBytes(sz);
- }
}
private static class EmptyPackObjectSizeIndex
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 ef9753cd79..720a3bcbff 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
@@ -75,20 +75,20 @@ public interface PackReverseIndex {
throws CorruptObjectException;
/**
- * Find the position in the primary index of the object at the given pack
+ * Find the position in the reverse index of the object at the given pack
* offset.
*
* @param offset
* the pack offset of the object
- * @return the position in the primary index of the object
+ * @return the position in the reverse index of the object
*/
int findPosition(long offset);
/**
- * Find the object that is in the given position in the primary index.
+ * Find the object that is in the given position in the reverse index.
*
* @param nthPosition
- * the position of the object in the primary index
+ * the position of the object in the reverse index
* @return the object in that position
*/
ObjectId findObjectByPosition(int nthPosition);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index 8e57bf9f2f..05f1ef53a1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -16,6 +16,7 @@ package org.eclipse.jgit.internal.storage.file;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.HEAD;
import static org.eclipse.jgit.lib.Constants.LOGS;
+import static org.eclipse.jgit.lib.Constants.L_LOGS;
import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
import static org.eclipse.jgit.lib.Constants.PACKED_REFS;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
@@ -56,23 +57,25 @@ import java.util.stream.Stream;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.PackRefsCommand;
import org.eclipse.jgit.errors.InvalidObjectIdException;
import org.eclipse.jgit.errors.LockFailedException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.ObjectWritingException;
import org.eclipse.jgit.events.RefsChangedEvent;
import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.CoreConfig.TrustLooseRefStat;
-import org.eclipse.jgit.lib.CoreConfig.TrustPackedRefsStat;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.TrustStat;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefComparator;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefWriter;
+import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.SymbolicRef;
import org.eclipse.jgit.revwalk.RevObject;
@@ -124,6 +127,8 @@ public class RefDirectory extends RefDatabase {
private final File gitDir;
+ private final File gitCommonDir;
+
final File refsDir;
final File packedRefsFile;
@@ -179,24 +184,19 @@ public class RefDirectory extends RefDatabase {
private List<Integer> retrySleepMs = RETRY_SLEEP_MS;
- private final boolean trustFolderStat;
-
- private final TrustPackedRefsStat trustPackedRefsStat;
-
- private final TrustLooseRefStat trustLooseRefStat;
+ private final CoreConfig coreConfig;
RefDirectory(RefDirectory refDb) {
parent = refDb.parent;
gitDir = refDb.gitDir;
+ gitCommonDir = refDb.gitCommonDir;
refsDir = refDb.refsDir;
logsDir = refDb.logsDir;
logsRefsDir = refDb.logsRefsDir;
packedRefsFile = refDb.packedRefsFile;
looseRefs.set(refDb.looseRefs.get());
packedRefs.set(refDb.packedRefs.get());
- trustFolderStat = refDb.trustFolderStat;
- trustPackedRefsStat = refDb.trustPackedRefsStat;
- trustLooseRefStat = refDb.trustLooseRefStat;
+ coreConfig = refDb.coreConfig;
inProcessPackedRefsLock = refDb.inProcessPackedRefsLock;
}
@@ -204,24 +204,15 @@ public class RefDirectory extends RefDatabase {
final FS fs = db.getFS();
parent = db;
gitDir = db.getDirectory();
- refsDir = fs.resolve(gitDir, R_REFS);
- logsDir = fs.resolve(gitDir, LOGS);
- logsRefsDir = fs.resolve(gitDir, LOGS + '/' + R_REFS);
- packedRefsFile = fs.resolve(gitDir, PACKED_REFS);
+ gitCommonDir = db.getCommonDirectory();
+ refsDir = fs.resolve(gitCommonDir, R_REFS);
+ logsDir = fs.resolve(gitCommonDir, LOGS);
+ logsRefsDir = fs.resolve(gitCommonDir, L_LOGS + R_REFS);
+ packedRefsFile = fs.resolve(gitCommonDir, PACKED_REFS);
looseRefs.set(RefList.<LooseRef> emptyList());
packedRefs.set(NO_PACKED_REFS);
- trustFolderStat = db.getConfig()
- .getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
- ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
- trustPackedRefsStat = db.getConfig()
- .getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_TRUST_PACKED_REFS_STAT,
- TrustPackedRefsStat.UNSET);
- trustLooseRefStat = db.getConfig()
- .getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_TRUST_LOOSE_REF_STAT,
- TrustLooseRefStat.ALWAYS);
+ coreConfig = db.getConfig().get(CoreConfig.KEY);
inProcessPackedRefsLock = new ReentrantLock(true);
}
@@ -282,6 +273,33 @@ public class RefDirectory extends RefDatabase {
clearReferences();
}
+ /**
+ * {@inheritDoc}
+ *
+ * For a RefDirectory database, by default this packs non-symbolic, loose
+ * tag refs into packed-refs. If {@code all} flag is set, this packs all the
+ * non-symbolic, loose refs.
+ */
+ @Override
+ public void packRefs(ProgressMonitor pm, PackRefsCommand packRefs)
+ throws IOException {
+ String prefix = packRefs.isAll() ? R_REFS : R_TAGS;
+ Collection<Ref> refs = getRefsByPrefix(prefix);
+ List<String> refsToBePacked = new ArrayList<>(refs.size());
+ pm.beginTask(JGitText.get().packRefs, refs.size());
+ try {
+ for (Ref ref : refs) {
+ if (!ref.isSymbolic() && ref.getStorage().isLoose()) {
+ refsToBePacked.add(ref.getName());
+ }
+ pm.update(1);
+ }
+ pack(refsToBePacked);
+ } finally {
+ pm.endTask();
+ }
+ }
+
@Override
public boolean isNameConflicting(String name) throws IOException {
// Cannot be nested within an existing reference.
@@ -422,6 +440,11 @@ public class RefDirectory extends RefDatabase {
return ret;
}
+ @Override
+ public ReflogReader getReflogReader(Ref ref) throws IOException {
+ return new ReflogReaderImpl(getRepository(), ref.getName());
+ }
+
@SuppressWarnings("unchecked")
private RefList<Ref> upcast(RefList<? extends Ref> loose) {
return (RefList<Ref>) loose;
@@ -939,7 +962,7 @@ public class RefDirectory extends RefDatabase {
PackedRefList getPackedRefs() throws IOException {
final PackedRefList curList = packedRefs.get();
- switch (trustPackedRefsStat) {
+ switch (coreConfig.getTrustPackedRefsStat()) {
case NEVER:
break;
case AFTER_OPEN:
@@ -955,12 +978,8 @@ public class RefDirectory extends RefDatabase {
return curList;
}
break;
- case UNSET:
- if (trustFolderStat
- && !curList.snapshot.isModified(packedRefsFile)) {
- return curList;
- }
- break;
+ case INHERIT:
+ // only used in CoreConfig internally
}
return refreshPackedRefs(curList);
@@ -1146,7 +1165,7 @@ public class RefDirectory extends RefDatabase {
LooseRef scanRef(LooseRef ref, String name) throws IOException {
final File path = fileFor(name);
- if (trustLooseRefStat.equals(TrustLooseRefStat.AFTER_OPEN)) {
+ if (coreConfig.getTrustLooseRefStat() == TrustStat.AFTER_OPEN) {
refreshPathToLooseRef(Paths.get(name));
}
@@ -1329,7 +1348,12 @@ public class RefDirectory extends RefDatabase {
name = name.substring(R_REFS.length());
return new File(refsDir, name);
}
- return new File(gitDir, name);
+ // HEAD needs to get resolved from git dir as resolving it from common dir
+ // would always lead back to current default branch
+ if (name.equals(HEAD)) {
+ return new File(gitDir, name);
+ }
+ return new File(gitCommonDir, name);
}
static int levelsIn(String name) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
index 21b5a54eb7..f1888eb90f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
@@ -10,6 +10,8 @@
package org.eclipse.jgit.internal.storage.file;
+import static org.eclipse.jgit.lib.Constants.HEAD;
+
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -37,7 +39,9 @@ class ReflogReaderImpl implements ReflogReader {
* {@code Ref} name
*/
ReflogReaderImpl(Repository db, String refname) {
- logName = new File(db.getDirectory(), Constants.LOGS + '/' + refname);
+ File logBaseDir = refname.equals(HEAD) ? db.getDirectory()
+ : db.getCommonDirectory();
+ logName = new File(logBaseDir, Constants.L_LOGS + refname);
}
/* (non-Javadoc)
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 30f8240aa9..15c125c684 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
@@ -15,8 +15,10 @@ import java.io.IOException;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Map;
import java.util.Random;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -397,7 +399,11 @@ public class WindowCache {
}
static final void purge(Pack pack) {
- cache.removeAll(pack);
+ purge(Collections.singleton(pack));
+ }
+
+ static final void purge(Set<Pack> packs) {
+ cache.queueRemoveAll(packs);
}
/** cleanup released and/or garbage collected windows. */
@@ -441,6 +447,29 @@ public class WindowCache {
private final boolean useStrongIndexRefs;
+ /** Removers are purely CPU/mem bound (no I/O), so likely should not go above # CPUs */
+ private final int idealNumRemovers;
+
+ /** Number of blocks to split the Pack removal into.
+ *
+ * Consolidation is better with more blocks since it increases
+ * the wait before moving to the next set by allowing more work
+ * to accumulate in the next set. On the flip side, the more
+ * blocks, the more synchronization overhead increasing each
+ * removers latency.
+ */
+ private final int numRemovalBlocks;
+
+ private final int removalBlockSize;
+
+ private Set<Pack> packsToRemove = new HashSet<>();
+
+ private Set<Pack> packsBeingRemoved;
+
+ private int numRemovers;
+
+ private int blockBeingRemoved;
+
private WindowCache(WindowCacheConfig cfg) {
tableSize = tableSize(cfg);
final int lockCount = lockCount(cfg);
@@ -479,6 +508,23 @@ public class WindowCache {
statsRecorder = mbean;
publishMBean.set(cfg.getExposeStatsViaJmx());
+ /* Since each worker will only process up to one full set of blocks, at least 2
+ * workers are needed anytime there are queued removals to ensure that all the
+ * blocks will get processed. However, if workers are maxed out at only 2, then
+ * enough newer workers will never start in order to make it safe for older
+ * workers to quit early. At least 3 workers are needed to make older worker
+ * relief transitions possible.
+ */
+ idealNumRemovers = Math.max(3, Runtime.getRuntime().availableProcessors());
+
+ int bs = 1024;
+ if (tableSize < 2 * bs) {
+ bs = tableSize / 2;
+ }
+ removalBlockSize = bs;
+ numRemovalBlocks = tableSize / removalBlockSize;
+ blockBeingRemoved = numRemovalBlocks - 1;
+
if (maxFiles < 1)
throw new IllegalArgumentException(JGitText.get().openFilesMustBeAtLeast1);
if (maxBytes < windowSize)
@@ -708,22 +754,105 @@ public class WindowCache {
}
/**
- * Clear all entries related to a single file.
+ * Asynchronously clear all entries related to files.
* <p>
- * 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.
+ * Typically this method is invoked during {@link Pack#close()}, or
+ * {@link Pack#close(Set)}, when we know the packs are never going to be
+ * useful to us again (for example, they no longer exist on disk after gc).
+ * A concurrent reader loading an entry from these same packs may cause a
+ * pack to become stuck in the cache anyway.
*
- * @param pack
- * the file to purge all entries of.
+ * Work on clearing files will be split up into blocks so that removing
+ * can be shared by more than one thread. This potential work sharing
+ * can provide 2 optimizations for removals:
+ * <ol>
+ * <li> It provides an opportunity for separate removal requests to be
+ * consolidated into one removal pass.</li>
+ * <li> It can reduce removing thread latencies by sharing the removal work
+ * with other removing threads which otherwise might not have any work to do
+ * due to their removal request being consolidated. This makes the system
+ * more efficient and can actually reduce latencies as system load increases
+ * due to pack removals!</li>
+ * </ol>
+ * The optimizations above are all achieved without blockng threads to wait
+ * for other threads to complete (although naturally there are some
+ * synchronization points), and while ensuring that no threads do more work
+ * than if they were the only thread available to perform a removal.
+ *
+ * @param packs
+ * the files to purge all entries of
*/
- private void removeAll(Pack pack) {
- for (int s = 0; s < tableSize; s++) {
+ private void queueRemoveAll(Set<Pack> packs) {
+ synchronized (this) {
+ packsToRemove.addAll(packs);
+ if (numRemovers >= idealNumRemovers) {
+ return;
+ }
+ numRemovers++;
+ }
+ for (int numRemoved = 0; removeNextBlock(numRemoved); numRemoved++) {
+ // empty
+ }
+ synchronized (this) {
+ if (numRemovers > 0) {
+ numRemovers--;
+ }
+ }
+ }
+
+ /** Determine which block to remove next, if any, and do so.
+ *
+ * @param numRemoved
+ * the number of already processed block removals by the current thread
+ * @return whether more processing should be done by the current thread
+ */
+ private boolean removeNextBlock(int numRemoved) {
+ Set<Pack> toRemove;
+ int block;
+ synchronized (this) {
+ if (packsBeingRemoved == null || blockBeingRemoved >= numRemovalBlocks - 1) {
+ if (packsToRemove.isEmpty()) {
+ return false;
+ }
+
+ blockBeingRemoved = 0;
+ packsBeingRemoved = packsToRemove;
+ packsToRemove = new HashSet<>();
+ } else {
+ blockBeingRemoved++;
+ }
+
+ toRemove = packsBeingRemoved;
+ block = blockBeingRemoved;
+ }
+
+ removeBlock(toRemove, block);
+ numRemoved++;
+
+ /* Ensure threads never work on more than a full set of blocks (the equivalent
+ * of removing one Pack) */
+ boolean isLast = numRemoved >= numRemovalBlocks;
+ synchronized (this) {
+ if (numRemovers >= idealNumRemovers) {
+ isLast = true;
+ }
+ }
+ return !isLast;
+ }
+
+ /** Remove a block of entries for a Set of files
+ * @param packs
+ * the files to purge all entries of
+ * @param block
+ * the specific block to process removals for
+ */
+ private void removeBlock(Set<Pack> packs, int block) {
+ int starting = block * removalBlockSize;
+ for (int s = starting; s < starting + removalBlockSize && s < tableSize; s++) {
final Entry e1 = table.get(s);
boolean hasDead = false;
for (Entry e = e1; e != null; e = e.next) {
- if (e.ref.getPack() == pack) {
+ if (packs.contains(e.ref.getPack())) {
e.kill();
hasDead = true;
} else if (e.dead)
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 01f514b939..11c45471e4 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
@@ -205,7 +205,7 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
* @param cnt
* number of bytes to copy. This value may exceed the number of
* bytes remaining in the window starting at offset
- * <code>pos</code>.
+ * <code>position</code>.
* @return number of bytes actually copied; this may be less than
* <code>cnt</code> if <code>cnt</code> exceeded the number of bytes
* available.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexConstants.java
new file mode 100644
index 0000000000..6122a9a143
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexConstants.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * 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.midx;
+
+class MultiPackIndexConstants {
+ static final int MIDX_SIGNATURE = 0x4d494458; /* MIDX */
+
+ static final byte MIDX_VERSION = 1;
+
+ /**
+ * We infer the length of object IDs (OIDs) from this value:
+ *
+ * <pre>
+ * 1 => SHA-1
+ * 2 => SHA-256
+ * </pre>
+ */
+ static final byte OID_HASH_VERSION = 1;
+
+ static final int MULTIPACK_INDEX_FANOUT_SIZE = 4 * 256;
+
+ /**
+ * First 4 bytes describe the chunk id. Value 0 is a terminating label.
+ * Other 8 bytes provide the byte-offset in current file for chunk to start.
+ */
+ static final int CHUNK_LOOKUP_WIDTH = 12;
+
+ /** "PNAM" chunk */
+ static final int MIDX_CHUNKID_PACKNAMES = 0x504e414d;
+
+ /** "OIDF" chunk */
+ static final int MIDX_CHUNKID_OIDFANOUT = 0x4f494446;
+
+ /** "OIDL" chunk */
+ static final int MIDX_CHUNKID_OIDLOOKUP = 0x4f49444c;
+
+ /** "OOFF" chunk */
+ static final int MIDX_CHUNKID_OBJECTOFFSETS = 0x4f4f4646;
+
+ /** "LOFF" chunk */
+ static final int MIDX_CHUNKID_LARGEOFFSETS = 0x4c4f4646;
+
+ /** "RIDX" chunk */
+ static final int MIDX_CHUNKID_REVINDEX = 0x52494458;
+
+ private MultiPackIndexConstants() {
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexPrettyPrinter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexPrettyPrinter.java
new file mode 100644
index 0000000000..795d39e375
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexPrettyPrinter.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * 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.midx;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.CHUNK_LOOKUP_WIDTH;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.util.NB;
+
+/**
+ * Prints a multipack index file in a human-readable format.
+ *
+ * @since 7.2
+ */
+@SuppressWarnings({ "boxing", "nls" })
+public class MultiPackIndexPrettyPrinter {
+
+ /**
+ * Writes to out, in human-readable format, the multipack index in rawMidx
+ *
+ * @param rawMidx the bytes of a multipack index
+ * @param out a writer
+ */
+ public static void prettyPrint(byte[] rawMidx, PrintWriter out) {
+ // Header (12 bytes)
+ out.println("[ 0] Magic: " + new String(rawMidx, 0, 4, UTF_8));
+ out.println("[ 4] Version number: " + (int) rawMidx[4]);
+ out.println("[ 5] OID version: " + (int) rawMidx[5]);
+ int chunkCount = rawMidx[6];
+ out.println("[ 6] # of chunks: " + chunkCount);
+ out.println("[ 7] # of bases: " + (int) rawMidx[7]);
+ int numberOfPacks = NB.decodeInt32(rawMidx, 8);
+ out.println("[ 8] # of packs: " + numberOfPacks);
+
+ // Chunk lookup table
+ List<ChunkSegment> chunkSegments = new ArrayList<>();
+ int current = printChunkLookup(out, rawMidx, chunkCount, chunkSegments);
+
+ for (int i = 0; i < chunkSegments.size() - 1; i++) {
+ ChunkSegment segment = chunkSegments.get(i);
+ if (current != segment.startOffset()) {
+ throw new IllegalStateException(String.format(
+ "We are at byte %d, but segment should start at %d",
+ current, segment.startOffset()));
+ }
+ out.printf("Starting chunk: %s @ %d%n", segment.chunkName(),
+ segment.startOffset());
+ switch (segment.chunkName()) {
+ case "OIDF" -> current = printOIDF(out, rawMidx, current);
+ case "OIDL" -> current = printOIDL(out, rawMidx, current,
+ chunkSegments.get(i + 1).startOffset);
+ case "OOFF" -> current = printOOFF(out, rawMidx, current,
+ chunkSegments.get(i + 1).startOffset);
+ case "PNAM" -> current = printPNAM(out, rawMidx, current,
+ chunkSegments.get(i + 1).startOffset);
+ case "RIDX" -> current = printRIDX(out, rawMidx, current,
+ chunkSegments.get(i + 1).startOffset);
+ default -> {
+ out.printf(
+ "Skipping %s (don't know how to print it yet)%n",
+ segment.chunkName());
+ current = (int) chunkSegments.get(i + 1).startOffset();
+ }
+ }
+ }
+ // Checksum is a SHA-1, use ObjectId to parse it
+ out.printf("[ %d] Checksum %s%n", current,
+ ObjectId.fromRaw(rawMidx, current).name());
+ out.printf("Total size: " + (current + 20));
+ }
+
+ private static int printChunkLookup(PrintWriter out, byte[] rawMidx, int chunkCount,
+ List<ChunkSegment> chunkSegments) {
+ out.println("Starting chunk lookup @ 12");
+ int current = 12;
+ for (int i = 0; i < chunkCount; i++) {
+ String chunkName = new String(rawMidx, current, 4, UTF_8);
+ long offset = NB.decodeInt64(rawMidx, current + 4);
+ out.printf("[ %d] |%8s|%8d|%n", current, chunkName, offset);
+ current += CHUNK_LOOKUP_WIDTH;
+ chunkSegments.add(new ChunkSegment(chunkName, offset));
+ }
+ String chunkName = "0000";
+ long offset = NB.decodeInt64(rawMidx, current + 4);
+ out.printf("[ %d] |%8s|%8d|%n", current, chunkName, offset);
+ current += CHUNK_LOOKUP_WIDTH;
+ chunkSegments.add(new ChunkSegment(chunkName, offset));
+ return current;
+ }
+
+ private static int printOIDF(PrintWriter out, byte[] rawMidx, int start) {
+ int current = start;
+ for (short i = 0; i < 256; i++) {
+ out.printf("[ %d] (%02X) %d%n", current, i,
+ NB.decodeInt32(rawMidx, current));
+ current += 4;
+ }
+ return current;
+ }
+
+ private static int printOIDL(PrintWriter out, byte[] rawMidx, int start, long end) {
+ int i = start;
+ while (i < end) {
+ out.printf("[ %d] %s%n", i,
+ ObjectId.fromRaw(rawMidx, i).name());
+ i += 20;
+ }
+ return i;
+ }
+
+ private static int printOOFF(PrintWriter out, byte[] rawMidx, int start, long end) {
+ int i = start;
+ while (i < end) {
+ out.printf("[ %d] %d %d%n", i, NB.decodeInt32(rawMidx, i),
+ NB.decodeInt32(rawMidx, i + 4));
+ i += 8;
+ }
+ return i;
+ }
+
+ private static int printRIDX(PrintWriter out, byte[] rawMidx, int start, long end) {
+ int i = start;
+ while (i < end) {
+ out.printf("[ %d] %d%n", i, NB.decodeInt32(rawMidx, i));
+ i += 4;
+ }
+ return (int) end;
+ }
+
+ private static int printPNAM(PrintWriter out, byte[] rawMidx, int start, long end) {
+ int nameStart = start;
+ for (int i = start; i < end; i++) {
+ if (rawMidx[i] == 0) {
+ out
+ .println(new String(rawMidx, nameStart, i - nameStart));
+ nameStart = i + 1;
+ }
+ }
+ return (int) end;
+ }
+
+ private record ChunkSegment(String chunkName, long startOffset) {
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriter.java
new file mode 100644
index 0000000000..bddf3ac4ad
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriter.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * 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.midx;
+
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.CHUNK_LOOKUP_WIDTH;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_LARGEOFFSETS;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OBJECTOFFSETS;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OIDFANOUT;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OIDLOOKUP;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_PACKNAMES;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_REVINDEX;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_SIGNATURE;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_VERSION;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MULTIPACK_INDEX_FANOUT_SIZE;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.OID_HASH_VERSION;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.io.CancellableDigestOutputStream;
+import org.eclipse.jgit.internal.storage.midx.PackIndexMerger.MidxMutableEntry;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.util.NB;
+
+/**
+ * Writes a collection of indexes as a multipack index.
+ * <p>
+ * See <a href=
+ * "https://git-scm.com/docs/pack-format#_multi_pack_index_midx_files_have_the_following_format">multipack
+ * index format spec</a>
+ *
+ * @since 7.2
+ */
+public class MultiPackIndexWriter {
+
+ private static final int LIMIT_31_BITS = (1 << 31) - 1;
+
+ private static final int MIDX_HEADER_SIZE = 12;
+
+ /**
+ * Writes the inputs in the multipack index format in the outputStream.
+ *
+ * @param monitor
+ * progress monitor
+ * @param outputStream
+ * stream to write the multipack index file
+ * @param inputs
+ * pairs of name and index for each pack to include in the
+ * multipack index.
+ * @throws IOException
+ * Error writing to the stream
+ */
+ public void write(ProgressMonitor monitor, OutputStream outputStream,
+ Map<String, PackIndex> inputs) throws IOException {
+ PackIndexMerger data = new PackIndexMerger(inputs);
+
+ // List of chunks in the order they need to be written
+ List<ChunkHeader> chunkHeaders = createChunkHeaders(data);
+ long expectedSize = calculateExpectedSize(chunkHeaders);
+ try (CancellableDigestOutputStream out = new CancellableDigestOutputStream(
+ monitor, outputStream)) {
+ writeHeader(out, chunkHeaders.size(), data.getPackCount());
+ writeChunkLookup(out, chunkHeaders);
+
+ WriteContext ctx = new WriteContext(out, data);
+ for (ChunkHeader chunk : chunkHeaders) {
+ chunk.writerFn.write(ctx);
+ }
+ writeCheckSum(out);
+ if (expectedSize != out.length()) {
+ throw new IllegalStateException(String.format(
+ JGitText.get().multiPackIndexUnexpectedSize,
+ Long.valueOf(expectedSize),
+ Long.valueOf(out.length())));
+ }
+ } catch (InterruptedIOException e) {
+ throw new IOException(JGitText.get().multiPackIndexWritingCancelled,
+ e);
+ }
+ }
+
+ private static long calculateExpectedSize(List<ChunkHeader> chunks) {
+ int chunkLookup = (chunks.size() + 1) * CHUNK_LOOKUP_WIDTH;
+ long chunkContent = chunks.stream().mapToLong(c -> c.size).sum();
+ return /* header */ 12 + chunkLookup + chunkContent + /* CRC */ 20;
+ }
+
+ private List<ChunkHeader> createChunkHeaders(PackIndexMerger data) {
+ List<ChunkHeader> chunkHeaders = new ArrayList<>();
+ chunkHeaders.add(new ChunkHeader(MIDX_CHUNKID_OIDFANOUT,
+ MULTIPACK_INDEX_FANOUT_SIZE, this::writeFanoutTable));
+ chunkHeaders.add(new ChunkHeader(MIDX_CHUNKID_OIDLOOKUP,
+ (long) data.getUniqueObjectCount() * OBJECT_ID_LENGTH,
+ this::writeOidLookUp));
+ chunkHeaders.add(new ChunkHeader(MIDX_CHUNKID_OBJECTOFFSETS,
+ 8L * data.getUniqueObjectCount(), this::writeObjectOffsets));
+ if (data.needsLargeOffsetsChunk()) {
+ chunkHeaders.add(new ChunkHeader(MIDX_CHUNKID_LARGEOFFSETS,
+ 8L * data.getOffsetsOver31BitsCount(),
+ this::writeObjectLargeOffsets));
+ }
+ chunkHeaders.add(new ChunkHeader(MIDX_CHUNKID_REVINDEX,
+ 4L * data.getUniqueObjectCount(), this::writeRidx));
+
+ int packNamesSize = data.getPackNames().stream()
+ .mapToInt(String::length).map(i -> i + 1 /* null at the end */)
+ .sum();
+ chunkHeaders.add(new ChunkHeader(MIDX_CHUNKID_PACKNAMES, packNamesSize,
+ this::writePackfileNames));
+ return chunkHeaders;
+ }
+
+ /**
+ * Write the first 12 bytes of the multipack index.
+ * <p>
+ * These bytes include things like magic number, version, number of
+ * chunks...
+ *
+ * @param out
+ * output stream to write
+ * @param numChunks
+ * number of chunks this multipack index is going to have
+ * @param packCount
+ * number of packs covered by this multipack index
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writeHeader(CancellableDigestOutputStream out, int numChunks,
+ int packCount) throws IOException {
+ byte[] headerBuffer = new byte[MIDX_HEADER_SIZE];
+ NB.encodeInt32(headerBuffer, 0, MIDX_SIGNATURE);
+ byte[] buff = { MIDX_VERSION, OID_HASH_VERSION, (byte) numChunks,
+ (byte) 0 };
+ System.arraycopy(buff, 0, headerBuffer, 4, 4);
+ NB.encodeInt32(headerBuffer, 8, packCount);
+ out.write(headerBuffer, 0, headerBuffer.length);
+ out.flush();
+ }
+
+ /**
+ * Write a table of "chunkId, start-offset", with a special value "0,
+ * end-of-previous_chunk", to mark the end.
+ *
+ * @param out
+ * output stream to write
+ * @param chunkHeaders
+ * list of chunks in the order they are expected to be written
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writeChunkLookup(CancellableDigestOutputStream out,
+ List<ChunkHeader> chunkHeaders) throws IOException {
+
+ // first chunk will start at header + this lookup block
+ long chunkStart = MIDX_HEADER_SIZE
+ + (long) (chunkHeaders.size() + 1) * CHUNK_LOOKUP_WIDTH;
+ byte[] chunkEntry = new byte[CHUNK_LOOKUP_WIDTH];
+ for (ChunkHeader chunkHeader : chunkHeaders) {
+ NB.encodeInt32(chunkEntry, 0, chunkHeader.chunkId);
+ NB.encodeInt64(chunkEntry, 4, chunkStart);
+ out.write(chunkEntry);
+ chunkStart += chunkHeader.size;
+ }
+ // Terminating label for the block
+ // (chunkid 0, offset where the next block would start)
+ NB.encodeInt32(chunkEntry, 0, 0);
+ NB.encodeInt64(chunkEntry, 4, chunkStart);
+ out.write(chunkEntry);
+ }
+
+ /**
+ * Write the fanout table for the object ids
+ * <p>
+ * Table with 256 entries (one byte), where the ith entry, F[i], stores the
+ * number of OIDs with first byte at most i. Thus, F[255] stores the total
+ * number of objects.
+ *
+ * @param ctx
+ * write context
+ * @throws IOException
+ * error writing to the output stream
+ */
+
+ private void writeFanoutTable(WriteContext ctx) throws IOException {
+ byte[] tmp = new byte[4];
+ int[] fanout = new int[256];
+ Iterator<MidxMutableEntry> iterator = ctx.data.bySha1Iterator();
+ while (iterator.hasNext()) {
+ MidxMutableEntry e = iterator.next();
+ fanout[e.getObjectId().getFirstByte() & 0xff]++;
+ }
+ for (int i = 1; i < fanout.length; i++) {
+ fanout[i] += fanout[i - 1];
+ }
+ for (int n : fanout) {
+ NB.encodeInt32(tmp, 0, n);
+ ctx.out.write(tmp, 0, 4);
+ }
+ }
+
+ /**
+ * Write the OID lookup chunk
+ * <p>
+ * A list of OIDs in sha1 order.
+ *
+ * @param ctx
+ * write context
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writeOidLookUp(WriteContext ctx) throws IOException {
+ byte[] tmp = new byte[OBJECT_ID_LENGTH];
+
+ Iterator<MidxMutableEntry> iterator = ctx.data.bySha1Iterator();
+ while (iterator.hasNext()) {
+ MidxMutableEntry e = iterator.next();
+ e.getObjectId().copyRawTo(tmp, 0);
+ ctx.out.write(tmp, 0, OBJECT_ID_LENGTH);
+ }
+ }
+
+ /**
+ * Write the object offsets chunk
+ * <p>
+ * A list of offsets, parallel to the list of OIDs. If the offset is too
+ * large (see {@link #fitsIn31bits(long)}), this contains the position in
+ * the large offsets list (marked with a 1 in the most significant bit).
+ *
+ * @param ctx
+ * write context
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writeObjectOffsets(WriteContext ctx) throws IOException {
+ byte[] entry = new byte[8];
+ Iterator<MidxMutableEntry> iterator = ctx.data.bySha1Iterator();
+ while (iterator.hasNext()) {
+ MidxMutableEntry e = iterator.next();
+ NB.encodeInt32(entry, 0, e.getPackId());
+ if (!ctx.data.needsLargeOffsetsChunk()
+ || fitsIn31bits(e.getOffset())) {
+ NB.encodeInt32(entry, 4, (int) e.getOffset());
+ } else {
+ int offloadedPosition = ctx.largeOffsets.append(e.getOffset());
+ NB.encodeInt32(entry, 4, offloadedPosition | (1 << 31));
+ }
+ ctx.out.write(entry);
+ }
+ }
+
+ /**
+ * Writes the reverse index chunk
+ * <p>
+ * This stores the position of the objects in the main index, ordered first
+ * by pack and then by offset
+ *
+ * @param ctx
+ * write context
+ * @throws IOException
+ * erorr writing to the output stream
+ */
+ private void writeRidx(WriteContext ctx) throws IOException {
+ Map<Integer, List<OffsetPosition>> packOffsets = new HashMap<>(
+ ctx.data.getPackCount());
+ // TODO(ifrade): Brute force solution loading all offsets/packs in
+ // memory. We could also iterate reverse indexes looking up
+ // their position in the midx (and discarding if the pack doesn't
+ // match).
+ Iterator<MidxMutableEntry> iterator = ctx.data.bySha1Iterator();
+ int midxPosition = 0;
+ while (iterator.hasNext()) {
+ MidxMutableEntry e = iterator.next();
+ OffsetPosition op = new OffsetPosition(e.getOffset(), midxPosition);
+ midxPosition++;
+ packOffsets.computeIfAbsent(Integer.valueOf(e.getPackId()),
+ k -> new ArrayList<>()).add(op);
+ }
+
+ for (int i = 0; i < ctx.data.getPackCount(); i++) {
+ List<OffsetPosition> offsetsForPack = packOffsets
+ .get(Integer.valueOf(i));
+ if (offsetsForPack.isEmpty()) {
+ continue;
+ }
+ offsetsForPack.sort(Comparator.comparing(OffsetPosition::offset));
+ byte[] ridxForPack = new byte[4 * offsetsForPack.size()];
+ for (int j = 0; j < offsetsForPack.size(); j++) {
+ NB.encodeInt32(ridxForPack, j * 4,
+ offsetsForPack.get(j).position);
+ }
+ ctx.out.write(ridxForPack);
+ }
+ }
+
+ /**
+ * Write the large offset chunk
+ * <p>
+ * A list of large offsets (long). The regular offset chunk will point to a
+ * position here.
+ *
+ * @param ctx
+ * writer context
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writeObjectLargeOffsets(WriteContext ctx) throws IOException {
+ ctx.out.write(ctx.largeOffsets.offsets, 0,
+ ctx.largeOffsets.bytePosition);
+ }
+
+ /**
+ * Write the list of packfiles chunk
+ * <p>
+ * List of packfiles (in lexicographical order) with an \0 at the end
+ *
+ * @param ctx
+ * writer context
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writePackfileNames(WriteContext ctx) throws IOException {
+ for (String packName : ctx.data.getPackNames()) {
+ // Spec doesn't talk about encoding.
+ ctx.out.write(packName.getBytes(StandardCharsets.UTF_8));
+ ctx.out.write(0);
+ }
+ }
+
+ /**
+ * Write final checksum of the data written to the stream
+ *
+ * @param out
+ * output stream used to write
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writeCheckSum(CancellableDigestOutputStream out)
+ throws IOException {
+ out.write(out.getDigest());
+ out.flush();
+ }
+
+
+ private record OffsetPosition(long offset, int position) {
+ }
+
+ /**
+ * If there is at least one offset value larger than 2^32-1, then the large
+ * offset chunk must exist, and offsets larger than 2^31-1 must be stored in
+ * it instead
+ *
+ * @param offset object offset
+ *
+ * @return true if the offset fits in 31 bits
+ */
+ private static boolean fitsIn31bits(long offset) {
+ return offset <= LIMIT_31_BITS;
+ }
+
+ private static class LargeOffsets {
+ private final byte[] offsets;
+
+ private int bytePosition;
+
+ LargeOffsets(int largeOffsetsCount) {
+ offsets = new byte[largeOffsetsCount * 8];
+ bytePosition = 0;
+ }
+
+ /**
+ * Add an offset to the large offset chunk
+ *
+ * @param largeOffset
+ * a large offset
+ * @return the position of the just inserted offset (as in number of
+ * offsets, NOT in bytes)
+ */
+ int append(long largeOffset) {
+ int at = bytePosition;
+ NB.encodeInt64(offsets, at, largeOffset);
+ bytePosition += 8;
+ return at / 8;
+ }
+ }
+
+ private record ChunkHeader(int chunkId, long size, ChunkWriter writerFn) {
+ }
+
+ @FunctionalInterface
+ private interface ChunkWriter {
+ void write(WriteContext ctx) throws IOException;
+ }
+
+ private static class WriteContext {
+ final CancellableDigestOutputStream out;
+
+ final PackIndexMerger data;
+
+ final LargeOffsets largeOffsets;
+
+ WriteContext(CancellableDigestOutputStream out, PackIndexMerger data) {
+ this.out = out;
+ this.data = data;
+ this.largeOffsets = new LargeOffsets(
+ data.getOffsetsOver31BitsCount());
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java
new file mode 100644
index 0000000000..89814af107
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * 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.midx;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.stream.Collectors;
+
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.MutableObjectId;
+
+/**
+ * Collect the stats and offers an iterator over the union of n-pack indexes.
+ * <p>
+ * The multipack index is a list of (sha1, packid, offset) ordered by sha1. We
+ * can build it from the individual pack indexes (sha1, offset) ordered by sha1,
+ * with a simple merge ignoring duplicates.
+ * <p>
+ * This class encapsulates the merging logic and precalculates the stats that
+ * the index needs (like total count of objects). To limit memory consumption,
+ * it does the merge as it goes during the iteration and iterators use mutable
+ * entries. The stats of the combined index are calculated in an iteration at
+ * construction time.
+ */
+class PackIndexMerger {
+
+ private static final int LIMIT_31_BITS = (1 << 31) - 1;
+
+ private static final long LIMIT_32_BITS = (1L << 32) - 1;
+
+ /**
+ * Object returned by the iterator.
+ * <p>
+ * The iterator returns (on each next()) the same instance with different
+ * values, to avoid allocating many short-lived objects. Callers should not
+ * keep a reference to that returned value.
+ */
+ static class MidxMutableEntry {
+ // The object id
+ private final MutableObjectId oid = new MutableObjectId();
+
+ // Position of the pack in the ordered list of pack in this merger
+ private int packId;
+
+ // Offset in its pack
+ private long offset;
+
+ public AnyObjectId getObjectId() {
+ return oid;
+ }
+
+ public int getPackId() {
+ return packId;
+ }
+
+ public long getOffset() {
+ return offset;
+ }
+
+ /**
+ * Copy values from another mutable entry
+ *
+ * @param packId
+ * packId
+ * @param other
+ * another mutable entry
+ */
+ private void fill(int packId, PackIndex.MutableEntry other) {
+ other.copyOidTo(oid);
+ this.packId = packId;
+ this.offset = other.getOffset();
+ }
+ }
+
+ private final List<String> packNames;
+
+ private final List<PackIndex> indexes;
+
+ private final boolean needsLargeOffsetsChunk;
+
+ private final int offsetsOver31BitsCount;
+
+ private final int uniqueObjectCount;
+
+ PackIndexMerger(Map<String, PackIndex> packs) {
+ this.packNames = packs.keySet().stream().sorted()
+ .collect(Collectors.toUnmodifiableList());
+
+ this.indexes = packNames.stream().map(packs::get)
+ .collect(Collectors.toUnmodifiableList());
+
+ // Iterate for duplicates
+ int objectCount = 0;
+ boolean hasLargeOffsets = false;
+ int over31bits = 0;
+ MutableObjectId lastSeen = new MutableObjectId();
+ MultiIndexIterator it = new MultiIndexIterator(indexes);
+ while (it.hasNext()) {
+ MidxMutableEntry entry = it.next();
+ if (lastSeen.equals(entry.oid)) {
+ continue;
+ }
+ // If there is at least one offset value larger than 2^32-1, then
+ // the large offset chunk must exist, and offsets larger than
+ // 2^31-1 must be stored in it instead
+ if (entry.offset > LIMIT_32_BITS) {
+ hasLargeOffsets = true;
+ }
+ if (entry.offset > LIMIT_31_BITS) {
+ over31bits++;
+ }
+
+ lastSeen.fromObjectId(entry.oid);
+ objectCount++;
+ }
+ uniqueObjectCount = objectCount;
+ offsetsOver31BitsCount = over31bits;
+ needsLargeOffsetsChunk = hasLargeOffsets;
+ }
+
+ /**
+ * Object count of the merged index (i.e. without duplicates)
+ *
+ * @return object count of the merged index
+ */
+ int getUniqueObjectCount() {
+ return uniqueObjectCount;
+ }
+
+ /**
+ * If any object in any of the indexes has an offset over 2^32-1
+ *
+ * @return true if there is any object with offset > 2^32 -1
+ */
+ boolean needsLargeOffsetsChunk() {
+ return needsLargeOffsetsChunk;
+ }
+
+ /**
+ * How many object have offsets over 2^31-1
+ * <p>
+ * Per multipack index spec, IF there is large offset chunk, all this
+ * offsets should be there.
+ *
+ * @return number of objects with offsets over 2^31-1
+ */
+ int getOffsetsOver31BitsCount() {
+ return offsetsOver31BitsCount;
+ }
+
+ /**
+ * List of pack names in alphabetical order.
+ * <p>
+ * Order matters: In case of duplicates, the multipack index prefers the
+ * first package with it. This is in the same order we are using to
+ * prioritize duplicates.
+ *
+ * @return List of pack names, in the order used by the merge.
+ */
+ List<String> getPackNames() {
+ return packNames;
+ }
+
+ /**
+ * How many packs are being merged
+ *
+ * @return count of packs merged
+ */
+ int getPackCount() {
+ return packNames.size();
+ }
+
+ /**
+ * Iterator over the merged indexes in sha1 order without duplicates
+ * <p>
+ * The returned entry in the iterator is mutable, callers should NOT keep a
+ * reference to it.
+ *
+ * @return an iterator in sha1 order without duplicates.
+ */
+ Iterator<MidxMutableEntry> bySha1Iterator() {
+ return new DedupMultiIndexIterator(new MultiIndexIterator(indexes),
+ getUniqueObjectCount());
+ }
+
+ /**
+ * For testing. Iterate all entries, not skipping duplicates (stable order)
+ *
+ * @return an iterator of all objects in sha1 order, including duplicates.
+ */
+ Iterator<MidxMutableEntry> rawIterator() {
+ return new MultiIndexIterator(indexes);
+ }
+
+ /**
+ * Iterator over n-indexes in ObjectId order.
+ * <p>
+ * It returns duplicates if the same object id is in different indexes. Wrap
+ * it with {@link DedupMultiIndexIterator (Iterator, int)} to avoid
+ * duplicates.
+ */
+ private static final class MultiIndexIterator
+ implements Iterator<MidxMutableEntry> {
+
+ private final List<PackIndexPeekIterator> indexIterators;
+
+ private final MidxMutableEntry mutableEntry = new MidxMutableEntry();
+
+ MultiIndexIterator(List<PackIndex> indexes) {
+ this.indexIterators = new ArrayList<>(indexes.size());
+ for (int i = 0; i < indexes.size(); i++) {
+ PackIndexPeekIterator it = new PackIndexPeekIterator(i,
+ indexes.get(i));
+ // Position in the first element
+ if (it.next() != null) {
+ indexIterators.add(it);
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return !indexIterators.isEmpty();
+ }
+
+ @Override
+ public MidxMutableEntry next() {
+ PackIndexPeekIterator winner = null;
+ for (int index = 0; index < indexIterators.size(); index++) {
+ PackIndexPeekIterator current = indexIterators.get(index);
+ if (winner == null
+ || current.peek().compareBySha1To(winner.peek()) < 0) {
+ winner = current;
+ }
+ }
+
+ if (winner == null) {
+ throw new NoSuchElementException();
+ }
+
+ mutableEntry.fill(winner.getPackId(), winner.peek());
+ if (winner.next() == null) {
+ indexIterators.remove(winner);
+ };
+ return mutableEntry;
+ }
+ }
+
+ private static class DedupMultiIndexIterator
+ implements Iterator<MidxMutableEntry> {
+ private final MultiIndexIterator src;
+
+ private int remaining;
+
+ private final MutableObjectId lastOid = new MutableObjectId();
+
+ DedupMultiIndexIterator(MultiIndexIterator src, int totalCount) {
+ this.src = src;
+ this.remaining = totalCount;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return remaining > 0;
+ }
+
+ @Override
+ public MidxMutableEntry next() {
+ MidxMutableEntry next = src.next();
+ while (next != null && lastOid.equals(next.oid)) {
+ next = src.next();
+ }
+
+ if (next == null) {
+ throw new NoSuchElementException();
+ }
+
+ lastOid.fromObjectId(next.oid);
+ remaining--;
+ return next;
+ }
+ }
+
+ /**
+ * Convenience around the PackIndex iterator to read the current value
+ * multiple times without consuming it.
+ * <p>
+ * This is used to merge indexes in the multipack index, where we need to
+ * compare the current value between indexes multiple times to find the
+ * next.
+ * <p>
+ * We could also implement this keeping the position (int) and
+ * MutableEntry#getObjectId, but that would create an ObjectId per entry.
+ * This implementation reuses the MutableEntry and avoid instantiations.
+ */
+ // Visible for testing
+ static class PackIndexPeekIterator {
+ private final Iterator<PackIndex.MutableEntry> it;
+
+ private final int packId;
+
+ PackIndex.MutableEntry current;
+
+ PackIndexPeekIterator(int packId, PackIndex index) {
+ it = index.iterator();
+ this.packId = packId;
+ }
+
+ PackIndex.MutableEntry next() {
+ if (it.hasNext()) {
+ current = it.next();
+ } else {
+ current = null;
+ }
+ return current;
+ }
+
+ PackIndex.MutableEntry peek() {
+ return current;
+ }
+
+ int getPackId() {
+ return packId;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackIndexWriter.java
new file mode 100644
index 0000000000..f69e68d4ba
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackIndexWriter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024, Google LLC.
+ *
+ * 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.pack;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.jgit.transport.PackedObjectInfo;
+
+/**
+ * Represents a function that accepts a collection of objects to write into a
+ * primary pack index storage format.
+ */
+public interface PackIndexWriter {
+ /**
+ * Write all object entries to the index stream.
+ *
+ * @param toStore
+ * sorted list of objects to store in the index. The caller must
+ * have previously sorted the list using
+ * {@link org.eclipse.jgit.transport.PackedObjectInfo}'s native
+ * {@link java.lang.Comparable} implementation.
+ * @param packDataChecksum
+ * checksum signature of the entire pack data content. This is
+ * traditionally the last 20 bytes of the pack file's own stream.
+ * @throws java.io.IOException
+ * an error occurred while writing to the output stream, or the
+ * underlying format cannot store the object data supplied.
+ */
+ void write(List<? extends PackedObjectInfo> toStore,
+ byte[] packDataChecksum) throws IOException;
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
index 4350f97915..f025d4e3d5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
@@ -58,10 +58,10 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.SearchForReuseTimeout;
import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.internal.storage.file.PackIndexWriter;
+import org.eclipse.jgit.internal.storage.file.BasePackIndexWriter;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
import org.eclipse.jgit.internal.storage.file.PackObjectSizeIndexWriter;
import org.eclipse.jgit.internal.storage.file.PackReverseIndexWriter;
-import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.AsyncObjectSizeQueue;
import org.eclipse.jgit.lib.BatchingProgressMonitor;
@@ -118,7 +118,7 @@ import org.eclipse.jgit.util.TemporaryBuffer;
* {@link #preparePack(ProgressMonitor, Set, Set)}, and streaming with
* {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}. If the
* pack is being stored as a file the matching index can be written out after
- * writing the pack by {@link #writeIndex(OutputStream)}. An optional bitmap
+ * writing the pack by {@link #writeIndex(PackIndexWriter)}. An optional bitmap
* index can be made by calling {@link #prepareBitmapIndex(ProgressMonitor)}
* followed by {@link #writeBitmapIndex(PackBitmapIndexWriter)}.
* </p>
@@ -303,19 +303,6 @@ public class PackWriter implements AutoCloseable {
}
/**
- * Create a writer to load objects from the specified reader.
- * <p>
- * Objects for packing are specified in {@link #preparePack(Iterator)} or
- * {@link #preparePack(ProgressMonitor, Set, Set)}.
- *
- * @param reader
- * reader to read from the repository with.
- */
- public PackWriter(ObjectReader reader) {
- this(new PackConfig(), reader);
- }
-
- /**
* Create writer for specified repository.
* <p>
* Objects for packing are specified in {@link #preparePack(Iterator)} or
@@ -1091,7 +1078,7 @@ public class PackWriter implements AutoCloseable {
if (indexVersion <= 0) {
for (BlockList<ObjectToPack> objs : objectsLists)
indexVersion = Math.max(indexVersion,
- PackIndexWriter.oldestPossibleFormat(objs));
+ BasePackIndexWriter.oldestPossibleFormat(objs));
}
return indexVersion;
}
@@ -1112,12 +1099,28 @@ public class PackWriter implements AutoCloseable {
* the index data could not be written to the supplied stream.
*/
public void writeIndex(OutputStream indexStream) throws IOException {
+ writeIndex(BasePackIndexWriter.createVersion(indexStream,
+ getIndexVersion()));
+ }
+
+ /**
+ * Create an index file to match the pack file just written.
+ * <p>
+ * Called after
+ * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}.
+ * <p>
+ * Writing an index is only required for local pack storage. Packs sent on
+ * the network do not need to create an index.
+ *
+ * @param iw
+ * an {@link PackIndexWriter} instance to write the index
+ * @throws java.io.IOException
+ * the index data could not be written to the supplied stream.
+ */
+ public void writeIndex(PackIndexWriter iw) throws IOException {
if (isIndexDisabled())
throw new IOException(JGitText.get().cachedPacksPreventsIndexCreation);
-
long writeStart = System.currentTimeMillis();
- final PackIndexWriter iw = PackIndexWriter.createVersion(
- indexStream, getIndexVersion());
iw.write(sortByName(), packcsum);
stats.timeWriting += System.currentTimeMillis() - writeStart;
}
@@ -2461,7 +2464,7 @@ public class PackWriter implements AutoCloseable {
* object graph at selected commits. Writing a bitmap index is an optional
* feature that not all pack users may require.
* <p>
- * Called after {@link #writeIndex(OutputStream)}.
+ * Called after {@link #writeIndex(PackIndexWriter)}.
* <p>
* To reduce memory internal state is cleared during this method, rendering
* the PackWriter instance useless for anything further than a call to write
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
index dabc1f0c5f..bf87c4c9d6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
@@ -14,6 +14,7 @@ import static org.eclipse.jgit.internal.storage.file.PackBitmapIndex.FLAG_REUSE;
import static org.eclipse.jgit.revwalk.RevFlag.SEEN;
import java.io.IOException;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -28,16 +29,16 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.revwalk.AddUnseenToBitmapFilter;
import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl;
+import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl.CompressedBitmap;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndexRemapper;
-import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl.CompressedBitmap;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
import org.eclipse.jgit.revwalk.BitmapWalker;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -99,10 +100,10 @@ class PackWriterBitmapPreparer {
this.excessiveBranchCount = config.getBitmapExcessiveBranchCount();
this.excessiveBranchTipCount = Math.max(excessiveBranchCount,
config.getBitmapExcessiveBranchTipCount());
- long now = SystemReader.getInstance().getCurrentTime();
+ Instant now = SystemReader.getInstance().now();
long ageInSeconds = (long) config.getBitmapInactiveBranchAgeInDays()
* DAY_IN_SECONDS;
- this.inactiveBranchTimestamp = (now / 1000) - ageInSeconds;
+ this.inactiveBranchTimestamp = now.getEpochSecond() - ageInSeconds;
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
index d07713db8e..e9ff02700d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
@@ -32,6 +32,8 @@ import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -245,9 +247,9 @@ class BlockReader {
private PersonIdent readPersonIdent() {
String name = readValueString();
String email = readValueString();
- long ms = readVarint64() * 1000;
- int tz = readInt16();
- return new PersonIdent(name, email, ms, tz);
+ long epochSeconds = readVarint64();
+ ZoneOffset tz = ZoneOffset.ofTotalSeconds(readInt16() * 60);
+ return new PersonIdent(name, email, Instant.ofEpochSecond(epochSeconds), tz);
}
void readBlock(BlockSource src, long pos, int fileBlockSize)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
index 3e75a9dde3..542d6e94f3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
@@ -427,7 +427,35 @@ public class OpenSshConfigFile implements SshConfigStore {
return value;
}
- private static boolean patternMatchesHost(String pattern, String name) {
+ /**
+ * Tells whether a given {@code name} matches the given list of patterns,
+ * accounting for negative matches.
+ *
+ * @param patterns
+ * to test {@code name} against; any pattern starting with an
+ * exclamation mark is a negative pattern
+ * @param name
+ * to test
+ * @return {@code true} if the {@code name} matches at least one of the
+ * non-negative patterns and none of the negative patterns,
+ * {@code false} otherwise
+ * @since 7.1
+ */
+ public static boolean patternMatch(Iterable<String> patterns, String name) {
+ boolean doesMatch = false;
+ for (String pattern : patterns) {
+ if (pattern.startsWith("!")) { //$NON-NLS-1$
+ if (patternMatches(pattern.substring(1), name)) {
+ return false;
+ }
+ } else if (!doesMatch && patternMatches(pattern, name)) {
+ doesMatch = true;
+ }
+ }
+ return doesMatch;
+ }
+
+ private static boolean patternMatches(String pattern, String name) {
if (pattern.indexOf('*') >= 0 || pattern.indexOf('?') >= 0) {
final FileNameMatcher fn;
try {
@@ -680,18 +708,7 @@ public class OpenSshConfigFile implements SshConfigStore {
}
boolean matches(String hostName) {
- boolean doesMatch = false;
- for (String pattern : patterns) {
- if (pattern.startsWith("!")) { //$NON-NLS-1$
- if (patternMatchesHost(pattern.substring(1), hostName)) {
- return false;
- }
- } else if (!doesMatch
- && patternMatchesHost(pattern, hostName)) {
- doesMatch = true;
- }
- }
- return doesMatch;
+ return patternMatch(patterns, hostName);
}
private static String toKey(String key) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java
index 3447f669ab..270b760562 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java
@@ -53,24 +53,24 @@ public interface Optionally<T> {
/**
* The mutable optional object
*/
- protected T element;
+ protected Optional<T> optional;
/**
* @param element
* the mutable optional object
*/
public Hard(T element) {
- this.element = element;
+ optional = Optional.ofNullable(element);
}
@Override
public void clear() {
- element = null;
+ optional = Optional.empty();
}
@Override
public Optional<T> getOptional() {
- return Optional.ofNullable(element);
+ return optional;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbstractGpgSignatureVerifier.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbstractGpgSignatureVerifier.java
deleted file mode 100644
index 06a89dc535..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbstractGpgSignatureVerifier.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
-*
-* This program and the accompanying materials are made available under the
-* terms of the Eclipse Distribution License v. 1.0 which is available at
-* https://www.eclipse.org/org/documents/edl-v10.php.
-*
-* SPDX-License-Identifier: BSD-3-Clause
-*/
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevTag;
-import org.eclipse.jgit.util.RawParseUtils;
-
-/**
- * Provides a base implementation of
- * {@link GpgSignatureVerifier#verifySignature(RevObject, GpgConfig)}.
- *
- * @since 6.9
- */
-public abstract class AbstractGpgSignatureVerifier
- implements GpgSignatureVerifier {
-
- @Override
- public SignatureVerification verifySignature(RevObject object,
- 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.nextLfSkippingSplitLines(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(config, 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(config, data, signatureData);
- }
- return null;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
index c58133adab..f742e993a0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
@@ -35,23 +35,6 @@ public abstract class AnyObjectId implements Comparable<AnyObjectId> {
* @param secondObjectId
* the second identifier to compare. Must not be null.
* @return true if the two identifiers are the same.
- * @deprecated use {@link #isEqual(AnyObjectId, AnyObjectId)} instead
- */
- @Deprecated
- @SuppressWarnings("AmbiguousMethodReference")
- public static boolean equals(final AnyObjectId firstObjectId,
- final AnyObjectId secondObjectId) {
- return isEqual(firstObjectId, secondObjectId);
- }
-
- /**
- * Compare two object identifier byte sequences for equality.
- *
- * @param firstObjectId
- * the first identifier to compare. Must not be null.
- * @param secondObjectId
- * the second identifier to compare. Must not be null.
- * @return true if the two identifiers are the same.
* @since 5.4
*/
public static boolean isEqual(final AnyObjectId firstObjectId,
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 5dfb648faa..0c1da83dfb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
@@ -13,13 +13,17 @@ package org.eclipse.jgit.lib;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BARE;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_WORKTREE;
+import static org.eclipse.jgit.lib.Constants.CONFIG;
import static org.eclipse.jgit.lib.Constants.DOT_GIT;
+import static org.eclipse.jgit.lib.Constants.GITDIR_FILE;
import static org.eclipse.jgit.lib.Constants.GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY;
import static org.eclipse.jgit.lib.Constants.GIT_CEILING_DIRECTORIES_KEY;
+import static org.eclipse.jgit.lib.Constants.GIT_COMMON_DIR_KEY;
import static org.eclipse.jgit.lib.Constants.GIT_DIR_KEY;
import static org.eclipse.jgit.lib.Constants.GIT_INDEX_FILE_KEY;
import static org.eclipse.jgit.lib.Constants.GIT_OBJECT_DIRECTORY_KEY;
import static org.eclipse.jgit.lib.Constants.GIT_WORK_TREE_KEY;
+import static org.eclipse.jgit.lib.Constants.OBJECTS;
import java.io.File;
import java.io.IOException;
@@ -70,7 +74,21 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
&& ref[7] == ' ';
}
- private static File getSymRef(File workTree, File dotGit, FS fs)
+ /**
+ * Read symbolic reference file
+ *
+ * @param workTree
+ * the work tree path
+ * @param dotGit
+ * the .git file
+ * @param fs
+ * th FS util
+ * @return the file read from symbolic reference file
+ * @throws java.io.IOException
+ * the dotGit file is invalid reference
+ * @since 7.0
+ */
+ static File getSymRef(File workTree, File dotGit, FS fs)
throws IOException {
byte[] content = IO.readFully(dotGit);
if (!isSymRef(content)) {
@@ -102,6 +120,8 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
private File gitDir;
+ private File gitCommonDir;
+
private File objectDirectory;
private List<File> alternateObjectDirectories;
@@ -172,6 +192,30 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
}
/**
+ * Set common dir.
+ *
+ * @param gitCommonDir
+ * {@code GIT_COMMON_DIR}, the common repository meta directory.
+ * @return {@code this} (for chaining calls).
+ * @since 7.0
+ */
+ public B setGitCommonDir(File gitCommonDir) {
+ this.gitCommonDir = gitCommonDir;
+ this.config = null;
+ return self();
+ }
+
+ /**
+ * Get common dir.
+ *
+ * @return common dir; null if not set.
+ * @since 7.0
+ */
+ public File getGitCommonDir() {
+ return gitCommonDir;
+ }
+
+ /**
* Set the directory storing the repository's objects.
*
* @param objectDirectory
@@ -396,9 +440,9 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
* Read standard Git environment variables and configure from those.
* <p>
* This method tries to read the standard Git environment variables, such as
- * {@code GIT_DIR} and {@code GIT_WORK_TREE} to configure this builder
- * instance. If an environment variable is set, it overrides the value
- * already set in this builder.
+ * {@code GIT_DIR}, {@code GIT_COMMON_DIR}, {@code GIT_WORK_TREE} etc. to
+ * configure this builder instance. If an environment variable is set, it
+ * overrides the value already set in this builder.
*
* @return {@code this} (for chaining calls).
*/
@@ -410,9 +454,9 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
* Read standard Git environment variables and configure from those.
* <p>
* This method tries to read the standard Git environment variables, such as
- * {@code GIT_DIR} and {@code GIT_WORK_TREE} to configure this builder
- * instance. If a property is already set in the builder, the environment
- * variable is not used.
+ * {@code GIT_DIR}, {@code GIT_COMMON_DIR}, {@code GIT_WORK_TREE} etc. to
+ * configure this builder instance. If a property is already set in the
+ * builder, the environment variable is not used.
*
* @param sr
* the SystemReader abstraction to access the environment.
@@ -425,6 +469,13 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
setGitDir(new File(val));
}
+ if (getGitCommonDir() == null) {
+ String val = sr.getenv(GIT_COMMON_DIR_KEY);
+ if (val != null) {
+ setGitCommonDir(new File(val));
+ }
+ }
+
if (getObjectDirectory() == null) {
String val = sr.getenv(GIT_OBJECT_DIRECTORY_KEY);
if (val != null)
@@ -434,7 +485,7 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
if (getAlternateObjectDirectories() == null) {
String val = sr.getenv(GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY);
if (val != null) {
- for (String path : val.split(File.pathSeparator))
+ for (String path : val.split(File.pathSeparator, -1))
addAlternateObjectDirectory(new File(path));
}
}
@@ -454,7 +505,7 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
if (ceilingDirectories == null) {
String val = sr.getenv(GIT_CEILING_DIRECTORIES_KEY);
if (val != null) {
- for (String path : val.split(File.pathSeparator))
+ for (String path : val.split(File.pathSeparator, -1))
addCeilingDirectory(new File(path));
}
}
@@ -601,6 +652,7 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
public B setup() throws IllegalArgumentException, IOException {
requireGitDirOrWorkTree();
setupGitDir();
+ setupCommonDir();
setupWorkTree();
setupInternals();
return self();
@@ -658,6 +710,20 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
}
/**
+ * Perform standard common dir initialization.
+ *
+ * @throws java.io.IOException
+ * the repository could not be accessed
+ * @since 7.0
+ */
+ protected void setupCommonDir() throws IOException {
+ // no gitCommonDir? Try to get it from gitDir
+ if (getGitCommonDir() == null) {
+ setGitCommonDir(safeFS().getCommonDir(getGitDir()));
+ }
+ }
+
+ /**
* Perform standard work-tree initialization.
* <p>
* This is a method typically invoked inside of {@link #setup()}, near the
@@ -695,8 +761,12 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
* the repository could not be accessed
*/
protected void setupInternals() throws IOException {
- if (getObjectDirectory() == null && getGitDir() != null)
- setObjectDirectory(safeFS().resolve(getGitDir(), Constants.OBJECTS));
+ if (getObjectDirectory() == null) {
+ File commonDir = getGitCommonDir();
+ if (commonDir != null) {
+ setObjectDirectory(safeFS().resolve(commonDir, OBJECTS));
+ }
+ }
}
/**
@@ -723,12 +793,13 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
* the configuration is not available.
*/
protected Config loadConfig() throws IOException {
- if (getGitDir() != null) {
+ File commonDir = getGitCommonDir();
+ if (commonDir != null) {
// We only want the repository's configuration file, and not
// the user file, as these parameters must be unique to this
// repository and not inherited from other files.
//
- File path = safeFS().resolve(getGitDir(), Constants.CONFIG);
+ File path = safeFS().resolve(commonDir, CONFIG);
FileBasedConfig cfg = new FileBasedConfig(path, safeFS());
try {
cfg.load();
@@ -749,8 +820,29 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
//
String path = cfg.getString(CONFIG_CORE_SECTION, null,
CONFIG_KEY_WORKTREE);
- if (path != null)
+ if (path != null) {
return safeFS().resolve(getGitDir(), path).getCanonicalFile();
+ }
+
+ /*
+ * We are in worktree's $GIT_DIR folder
+ * ".git/worktrees/&lt;worktree-name&gt;" and want to get the working
+ * tree (checkout) path; so here we have an opposite link in file
+ * "gitdir" showing to the ".git" file located in the working tree read
+ * it and convert it to absolute path if it's relative
+ */
+ File gitDirFile = new File(getGitDir(), GITDIR_FILE);
+ if (gitDirFile.isFile()) {
+ String workDirPath = new String(IO.readFully(gitDirFile)).trim();
+ File workTreeDotGitFile = new File(workDirPath);
+ if (!workTreeDotGitFile.isAbsolute()) {
+ workTreeDotGitFile = new File(getGitDir(), workDirPath)
+ .getCanonicalFile();
+ }
+ if (workTreeDotGitFile != null) {
+ return workTreeDotGitFile.getParentFile();
+ }
+ }
// If core.bare is set, honor its value. Assume workTree is
// the parent directory of the repository.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java
index e15c7af932..7921052aaa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java
@@ -187,8 +187,7 @@ public class BranchConfig {
* @since 4.5
*/
public BranchRebaseMode getRebaseMode() {
- return config.getEnum(BranchRebaseMode.values(),
- ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
+ return config.getEnum(ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
ConfigConstants.CONFIG_KEY_REBASE, BranchRebaseMode.NONE);
}
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 ea33082229..ad3c2c091d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
@@ -194,19 +194,6 @@ public class CommitBuilder extends ObjectBuilder {
}
}
- /**
- * Set the encoding for the commit information.
- *
- * @param encodingName
- * the encoding name. See
- * {@link java.nio.charset.Charset#forName(String)}.
- * @deprecated use {@link #setEncoding(Charset)} instead.
- */
- @Deprecated
- public void setEncoding(String encodingName) {
- setEncoding(Charset.forName(encodingName));
- }
-
@Override
public byte[] build() throws UnsupportedEncodingException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java
index f701a41d67..b1ba5dfa28 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java
@@ -119,7 +119,7 @@ public class CommitConfig {
if (!StringUtils.isEmptyOrNull(comment)) {
if ("auto".equalsIgnoreCase(comment)) { //$NON-NLS-1$
autoCommentChar = true;
- } else {
+ } else if (comment != null) {
char first = comment.charAt(0);
if (first > ' ' && first < 127) {
commentCharacter = first;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index 07c5fa4500..345cb22f80 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -34,6 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.events.ConfigChangedEvent;
import org.eclipse.jgit.events.ConfigChangedListener;
@@ -254,9 +255,8 @@ public class Config {
* default value to return if no value was present.
* @return an integer value from the configuration, or defaultValue.
*/
- public int getInt(final String section, final String name,
- final int defaultValue) {
- return typedGetter.getInt(this, section, null, name, defaultValue);
+ public int getInt(String section, String name, int defaultValue) {
+ return getInt(section, null, name, defaultValue);
}
/**
@@ -264,6 +264,23 @@ public class Config {
*
* @param section
* section the key is grouped within.
+ * @param name
+ * name of the key to get.
+ * @return an integer value from the configuration, or {@code null} if not
+ * set.
+ * @since 7.2
+ */
+ @Nullable
+ public Integer getInt(String section, String name) {
+ return getInt(section, null, name);
+ }
+
+
+ /**
+ * Obtain an integer value from the configuration.
+ *
+ * @param section
+ * section the key is grouped within.
* @param subsection
* subsection name, such a remote or branch name.
* @param name
@@ -272,10 +289,30 @@ public class Config {
* default value to return if no value was present.
* @return an integer value from the configuration, or defaultValue.
*/
- public int getInt(final String section, String subsection,
- final String name, final int defaultValue) {
+ public int getInt(String section, String subsection, String name,
+ int defaultValue) {
+ Integer v = typedGetter.getInt(this, section, subsection, name,
+ Integer.valueOf(defaultValue));
+ return v == null ? defaultValue : v.intValue();
+ }
+
+ /**
+ * Obtain an integer value from the configuration.
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @return an integer value from the configuration, or {@code null} if not
+ * set.
+ * @since 7.2
+ */
+ @Nullable
+ public Integer getInt(String section, String subsection, String name) {
return typedGetter.getInt(this, section, subsection, name,
- defaultValue);
+ null);
}
/**
@@ -297,8 +334,30 @@ public class Config {
*/
public int getIntInRange(String section, String name, int minValue,
int maxValue, int defaultValue) {
- return typedGetter.getIntInRange(this, section, null, name, minValue,
- maxValue, defaultValue);
+ return getIntInRange(section, null, name,
+ minValue, maxValue, defaultValue);
+ }
+
+ /**
+ * Obtain an integer value from the configuration which must be inside given
+ * range.
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param name
+ * name of the key to get.
+ * @param minValue
+ * minimum value
+ * @param maxValue
+ * maximum value
+ * @return an integer value from the configuration, or {@code null} if not
+ * set.
+ * @since 7.2
+ */
+ @Nullable
+ public Integer getIntInRange(String section, String name, int minValue,
+ int maxValue) {
+ return getIntInRange(section, null, name, minValue, maxValue);
}
/**
@@ -322,8 +381,34 @@ public class Config {
*/
public int getIntInRange(String section, String subsection, String name,
int minValue, int maxValue, int defaultValue) {
+ Integer v = typedGetter.getIntInRange(this, section, subsection, name,
+ minValue, maxValue, Integer.valueOf(defaultValue));
+ return v == null ? defaultValue : v.intValue();
+ }
+
+ /**
+ * Obtain an integer value from the configuration which must be inside given
+ * range.
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @param minValue
+ * minimum value
+ * @param maxValue
+ * maximum value
+ * @return an integer value from the configuration, or {@code null} if not
+ * set.
+ * @since 7.2
+ */
+ @Nullable
+ public Integer getIntInRange(String section, String subsection, String name,
+ int minValue, int maxValue) {
return typedGetter.getIntInRange(this, section, subsection, name,
- minValue, maxValue, defaultValue);
+ minValue, maxValue, null);
}
/**
@@ -338,7 +423,23 @@ public class Config {
* @return an integer value from the configuration, or defaultValue.
*/
public long getLong(String section, String name, long defaultValue) {
- return typedGetter.getLong(this, section, null, name, defaultValue);
+ return getLong(section, null, name, defaultValue);
+ }
+
+ /**
+ * Obtain an integer value from the configuration.
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param name
+ * name of the key to get.
+ * @return an integer value from the configuration, or {@code null} if not
+ * set.
+ * @since 7.2
+ */
+ @Nullable
+ public Long getLong(String section, String name) {
+ return getLong(section, null, name);
}
/**
@@ -355,9 +456,28 @@ public class Config {
* @return an integer value from the configuration, or defaultValue.
*/
public long getLong(final String section, String subsection,
- final String name, final long defaultValue) {
- return typedGetter.getLong(this, section, subsection, name,
- defaultValue);
+ String name, long defaultValue) {
+ Long v = typedGetter.getLong(this, section, subsection, name,
+ Long.valueOf(defaultValue));
+ return v == null ? defaultValue : v.longValue();
+ }
+
+ /**
+ * Obtain an integer value from the configuration.
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @return an integer value from the configuration, or {@code null} if not
+ * set.
+ * @since 7.2
+ */
+ @Nullable
+ public Long getLong(String section, String subsection, String name) {
+ return typedGetter.getLong(this, section, subsection, name, null);
}
/**
@@ -372,9 +492,26 @@ public class Config {
* @return true if any value or defaultValue is true, false for missing or
* explicit false
*/
- public boolean getBoolean(final String section, final String name,
- final boolean defaultValue) {
- return typedGetter.getBoolean(this, section, null, name, defaultValue);
+ public boolean getBoolean(String section, String name,
+ boolean defaultValue) {
+ Boolean v = typedGetter.getBoolean(this, section, null, name,
+ Boolean.valueOf(defaultValue));
+ return v == null ? defaultValue : v.booleanValue();
+ }
+
+ /**
+ * Get a boolean value from the git config
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param name
+ * name of the key to get.
+ * @return configured boolean value, or {@code null} if not set.
+ * @since 7.2
+ */
+ @Nullable
+ public Boolean getBoolean(String section, String name) {
+ return getBoolean(section, null, name);
}
/**
@@ -391,10 +528,28 @@ public class Config {
* @return true if any value or defaultValue is true, false for missing or
* explicit false
*/
- public boolean getBoolean(final String section, String subsection,
- final String name, final boolean defaultValue) {
- return typedGetter.getBoolean(this, section, subsection, name,
- defaultValue);
+ public boolean getBoolean(String section, String subsection, String name,
+ boolean defaultValue) {
+ Boolean v = typedGetter.getBoolean(this, section, subsection, name,
+ Boolean.valueOf(defaultValue));
+ return v == null ? defaultValue : v.booleanValue();
+ }
+
+ /**
+ * Get a boolean value from the git config
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @return configured boolean value, or {@code null} if not set.
+ * @since 7.2
+ */
+ @Nullable
+ public Boolean getBoolean(String section, String subsection, String name) {
+ return typedGetter.getBoolean(this, section, subsection, name, null);
}
/**
@@ -412,8 +567,8 @@ public class Config {
* default value to return if no value was present.
* @return the selected enumeration value, or {@code defaultValue}.
*/
- public <T extends Enum<?>> T getEnum(final String section,
- final String subsection, final String name, final T defaultValue) {
+ public <T extends Enum<?>> T getEnum(String section, String subsection,
+ String name, @NonNull T defaultValue) {
final T[] all = allValuesOf(defaultValue);
return typedGetter.getEnum(this, all, section, subsection, name,
defaultValue);
@@ -448,14 +603,41 @@ public class Config {
* @param defaultValue
* default value to return if no value was present.
* @return the selected enumeration value, or {@code defaultValue}.
+ * @deprecated use {@link #getEnum(String, String, String, Enum)} or
+ * {{@link #getEnum(Enum[], String, String, String)}} instead.
*/
- public <T extends Enum<?>> T getEnum(final T[] all, final String section,
- final String subsection, final String name, final T defaultValue) {
+ @Nullable
+ @Deprecated
+ public <T extends Enum<?>> T getEnum(T[] all, String section,
+ String subsection, String name, @Nullable T defaultValue) {
return typedGetter.getEnum(this, all, section, subsection, name,
defaultValue);
}
/**
+ * Parse an enumeration from the configuration.
+ *
+ * @param <T>
+ * type of the returned enum
+ * @param all
+ * all possible values in the enumeration which should be
+ * recognized. Typically {@code EnumType.values()}.
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @return the selected enumeration value, or {@code null} if not set.
+ * @since 7.2
+ */
+ @Nullable
+ public <T extends Enum<?>> T getEnum(T[] all, String section,
+ String subsection, String name) {
+ return typedGetter.getEnum(this, all, section, subsection, name, null);
+ }
+
+ /**
* Get string value or null if not found.
*
* @param section
@@ -466,8 +648,8 @@ public class Config {
* the key name
* @return a String value from the config, <code>null</code> if not found
*/
- public String getString(final String section, String subsection,
- final String name) {
+ @Nullable
+ public String getString(String section, String subsection, String name) {
return getRawString(section, subsection, name);
}
@@ -526,8 +708,34 @@ public class Config {
*/
public long getTimeUnit(String section, String subsection, String name,
long defaultValue, TimeUnit wantUnit) {
+ Long v = typedGetter.getTimeUnit(this, section, subsection, name,
+ Long.valueOf(defaultValue), wantUnit);
+ return v == null ? defaultValue : v.longValue();
+
+ }
+
+ /**
+ * Parse a numerical time unit, such as "1 minute", from the configuration.
+ *
+ * @param section
+ * section the key is in.
+ * @param subsection
+ * subsection the key is in, or null if not in a subsection.
+ * @param name
+ * the key name.
+ * @param wantUnit
+ * the units of {@code defaultValue} and the return value, as
+ * well as the units to assume if the value does not contain an
+ * indication of the units.
+ * @return the value, or {@code null} if not set, expressed in
+ * {@code units}.
+ * @since 7.2
+ */
+ @Nullable
+ public Long getTimeUnit(String section, String subsection, String name,
+ TimeUnit wantUnit) {
return typedGetter.getTimeUnit(this, section, subsection, name,
- defaultValue, wantUnit);
+ null, wantUnit);
}
/**
@@ -555,8 +763,9 @@ public class Config {
* @return the {@link Path}, or {@code defaultValue} if not set
* @since 5.10
*/
+ @Nullable
public Path getPath(String section, String subsection, String name,
- @NonNull FS fs, File resolveAgainst, Path defaultValue) {
+ @NonNull FS fs, File resolveAgainst, @Nullable Path defaultValue) {
return typedGetter.getPath(this, section, subsection, name, fs,
resolveAgainst, defaultValue);
}
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 0edf3c5ad0..c4550329d3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -78,6 +78,13 @@ public final class ConfigConstants {
public static final String CONFIG_DFS_SECTION = "dfs";
/**
+ * The dfs cache subsection prefix.
+ *
+ * @since 7.0
+ */
+ public static final String CONFIG_DFS_CACHE_PREFIX = "dfs.";
+
+ /**
* The "receive" section
* @since 4.6
*/
@@ -199,7 +206,36 @@ public final class ConfigConstants {
public static final String CONFIG_KEY_SIGNINGKEY = "signingKey";
/**
+ * The "ssh" subsection key.
+ *
+ * @since 7.1
+ */
+ public static final String CONFIG_SSH_SUBSECTION = "ssh";
+
+ /**
+ * The "defaultKeyCommand" key.
+ *
+ * @since 7.1
+ */
+ public static final String CONFIG_KEY_SSH_DEFAULT_KEY_COMMAND = "defaultKeyCommand";
+
+ /**
+ * The "allowedSignersFile" key.
+ *
+ * @since 7.1
+ */
+ public static final String CONFIG_KEY_SSH_ALLOWED_SIGNERS_FILE = "allowedSignersFile";
+
+ /**
+ * The "revocationFile" key,
+ *
+ * @since 7.1
+ */
+ public static final String CONFIG_KEY_SSH_REVOCATION_FILE = "revocationFile";
+
+ /**
* The "commit" section
+ *
* @since 5.2
*/
public static final String CONFIG_COMMIT_SECTION = "commit";
@@ -332,6 +368,13 @@ public final class ConfigConstants {
public static final String CONFIG_KEY_DELTA_BASE_CACHE_LIMIT = "deltaBaseCacheLimit";
/**
+ * The "packExtensions" key
+ *
+ * @since 7.0
+ **/
+ public static final String CONFIG_KEY_PACK_EXTENSIONS = "packExtensions";
+
+ /**
* The "symlinks" key
* @since 3.3
*/
@@ -345,12 +388,6 @@ public final class ConfigConstants {
public static final String CONFIG_KEY_STREAM_FILE_THRESHOLD = "streamFileThreshold";
/**
- * @deprecated typo, use CONFIG_KEY_STREAM_FILE_THRESHOLD instead
- */
- @Deprecated(since = "6.8")
- public static final String CONFIG_KEY_STREAM_FILE_TRESHOLD = CONFIG_KEY_STREAM_FILE_THRESHOLD;
-
- /**
* The "packedGitMmap" key
* @since 5.1.13
*/
@@ -409,6 +446,13 @@ public final class ConfigConstants {
/** The "rebase" key */
public static final String CONFIG_KEY_REBASE = "rebase";
+ /**
+ * The "checkout" key
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_KEY_CHECKOUT = "checkout";
+
/** The "url" key */
public static final String CONFIG_KEY_URL = "url";
@@ -556,11 +600,21 @@ public final class ConfigConstants {
/**
* The "trustfolderstat" key in the "core" section
+ *
* @since 3.6
+ * @deprecated use {CONFIG_KEY_TRUST_STAT} instead
*/
+ @Deprecated(since = "7.2", forRemoval = true)
public static final String CONFIG_KEY_TRUSTFOLDERSTAT = "trustfolderstat";
/**
+ * The "trustfilestat" key in the "core"section
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_KEY_TRUST_STAT = "truststat";
+
+ /**
* The "supportsAtomicFileCreation" key in the "core" section
*
* @since 4.5
@@ -979,6 +1033,27 @@ public final class ConfigConstants {
public static final String CONFIG_KEY_TRUST_LOOSE_REF_STAT = "trustLooseRefStat";
/**
+ * The "trustLooseRefStat" key
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_KEY_TRUST_PACK_STAT = "trustPackStat";
+
+ /**
+ * The "trustLooseObjectFileStat" key
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_KEY_TRUST_LOOSE_OBJECT_STAT = "trustLooseObjectStat";
+
+ /**
+ * The "trustTablesListStat" key
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_KEY_TRUST_TABLESLIST_STAT = "trustTablesListStat";
+
+ /**
* The "pack.preserveOldPacks" key
*
* @since 5.13.2
@@ -1012,4 +1087,32 @@ public final class ConfigConstants {
* @since 6.7
*/
public static final String CONFIG_KEY_READ_CHANGED_PATHS = "readChangedPaths";
+
+ /**
+ * The "useObjectSizeIndex" key
+ *
+ * @since 7.0
+ */
+ public static final String CONFIG_KEY_USE_OBJECT_SIZE_INDEX = "useObjectSizeIndex";
+
+ /**
+ * The "loadRevIndexInParallel" key
+ *
+ * @since 7.1
+ */
+ public static final String CONFIG_KEY_LOAD_REV_INDEX_IN_PARALLEL = "loadRevIndexInParallel";
+
+ /**
+ * The "reftable" section
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_REFTABLE_SECTION = "reftable";
+
+ /**
+ * The "autorefresh" key
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_KEY_AUTOREFRESH = "autorefresh";
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index 60a23dde05..997f4ed314 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -12,10 +12,13 @@
package org.eclipse.jgit.lib;
+import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CodingErrorAction;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
@@ -203,24 +206,6 @@ public final class Constants {
*/
public static final byte[] PACK_SIGNATURE = { 'P', 'A', 'C', 'K' };
- /**
- * Native character encoding for commit messages, file names...
- *
- * @deprecated Use {@link java.nio.charset.StandardCharsets#UTF_8} directly
- * instead.
- */
- @Deprecated
- public static final Charset CHARSET;
-
- /**
- * Native character encoding for commit messages, file names...
- *
- * @deprecated Use {@link java.nio.charset.StandardCharsets#UTF_8} directly
- * instead.
- */
- @Deprecated
- public static final String CHARACTER_ENCODING;
-
/** Default main branch name */
public static final String MASTER = "master";
@@ -273,6 +258,20 @@ public final class Constants {
public static final String INFO_REFS = "info/refs";
/**
+ * Name of heads folder or file in refs.
+ *
+ * @since 7.0
+ */
+ public static final String HEADS = "heads";
+
+ /**
+ * Prefix for any log.
+ *
+ * @since 7.0
+ */
+ public static final String L_LOGS = LOGS + "/";
+
+ /**
* Info alternates file (goes under OBJECTS)
* @since 5.5
*/
@@ -358,6 +357,14 @@ public final class Constants {
public static final String GIT_DIR_KEY = "GIT_DIR";
/**
+ * The environment variable that tells us which directory is the common
+ * ".git" directory.
+ *
+ * @since 7.0
+ */
+ public static final String GIT_COMMON_DIR_KEY = "GIT_COMMON_DIR";
+
+ /**
* The environment variable that tells us which directory is the working
* directory.
*/
@@ -459,6 +466,36 @@ public final class Constants {
public static final String GITDIR = "gitdir: ";
/**
+ * Name of the file (inside gitDir) that references the worktree's .git
+ * file (opposite link).
+ *
+ * .git/worktrees/&lt;worktree-name&gt;/gitdir
+ *
+ * A text file containing the absolute path back to the .git file that
+ * points here. This file is used to verify if the linked repository has been
+ * manually removed in which case this directory is no longer needed.
+ * The modification time (mtime) of this file should be updated each time
+ * the linked repository is accessed.
+ *
+ * @since 7.0
+ */
+ public static final String GITDIR_FILE = "gitdir";
+
+ /**
+ * Name of the file (inside gitDir) that has reference to $GIT_COMMON_DIR.
+ *
+ * .git/worktrees/&lt;worktree-name&gt;/commondir
+ *
+ * If this file exists, $GIT_COMMON_DIR will be set to the path specified in
+ * this file unless it is explicitly set. If the specified path is relative,
+ * it is relative to $GIT_DIR. The repository with commondir is incomplete
+ * without the repository pointed by "commondir".
+ *
+ * @since 7.0
+ */
+ public static final String COMMONDIR_FILE = "commondir";
+
+ /**
* Name of the folder (inside gitDir) where submodules are stored
*
* @since 3.6
@@ -494,6 +531,34 @@ public final class Constants {
public static final String ATTR_BUILTIN_BINARY_MERGER = "binary"; //$NON-NLS-1$
/**
+ * Prefix of a GPG signature.
+ *
+ * @since 7.0
+ */
+ public static final String GPG_SIGNATURE_PREFIX = "-----BEGIN PGP SIGNATURE-----"; //$NON-NLS-1$
+
+ /**
+ * Prefix of a CMS signature (X.509, S/MIME).
+ *
+ * @since 7.0
+ */
+ public static final String CMS_SIGNATURE_PREFIX = "-----BEGIN SIGNED MESSAGE-----"; //$NON-NLS-1$
+
+ /**
+ * Prefix of an SSH signature.
+ *
+ * @since 7.0
+ */
+ public static final String SSH_SIGNATURE_PREFIX = "-----BEGIN SSH SIGNATURE-----"; //$NON-NLS-1$
+
+ /**
+ * Union built-in merge driver
+ *
+ * @since 6.10.1
+ */
+ public static final String ATTR_BUILTIN_UNION_MERGE_DRIVER = "union"; //$NON-NLS-1$
+
+ /**
* Create a new digest function for objects.
*
* @return a new digest object.
@@ -661,44 +726,32 @@ public final class Constants {
* the 7-bit ASCII character space.
*/
public static byte[] encodeASCII(String s) {
- final byte[] r = new byte[s.length()];
- for (int k = r.length - 1; k >= 0; k--) {
- final char c = s.charAt(k);
- if (c > 127)
- throw new IllegalArgumentException(MessageFormat.format(JGitText.get().notASCIIString, s));
- r[k] = (byte) c;
+ try {
+ CharsetEncoder encoder = US_ASCII.newEncoder()
+ .onUnmappableCharacter(CodingErrorAction.REPORT)
+ .onMalformedInput(CodingErrorAction.REPORT);
+ return encoder.encode(CharBuffer.wrap(s)).array();
+ } catch (CharacterCodingException e) {
+ throw new IllegalArgumentException(
+ MessageFormat.format(JGitText.get().notASCIIString, s), e);
}
- return r;
}
/**
- * Convert a string to a byte array in the standard character encoding.
+ * Convert a string to a byte array in the standard character encoding UTF8.
*
* @param str
* the string to convert. May contain any Unicode characters.
* @return a byte array representing the requested string, encoded using the
* default character encoding (UTF-8).
- * @see #CHARACTER_ENCODING
*/
public static byte[] encode(String str) {
- final ByteBuffer bb = UTF_8.encode(str);
- final int len = bb.limit();
- if (bb.hasArray() && bb.arrayOffset() == 0) {
- final byte[] arr = bb.array();
- if (arr.length == len)
- return arr;
- }
-
- final byte[] arr = new byte[len];
- bb.get(arr);
- return arr;
+ return str.getBytes(UTF_8);
}
static {
if (OBJECT_ID_LENGTH != newMessageDigest().getDigestLength())
throw new LinkageError(JGitText.get().incorrectOBJECT_ID_LENGTH);
- CHARSET = UTF_8;
- CHARACTER_ENCODING = UTF_8.name();
}
/** name of the file containing the commit msg for a merge commit */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
index 9fa5d75a3f..0e27b2743c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
@@ -17,12 +17,16 @@ package org.eclipse.jgit.lib;
import static java.util.zip.Deflater.DEFAULT_COMPRESSION;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Config.SectionParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* This class keeps git repository core parameters.
*/
public class CoreConfig {
+ private static final Logger LOG = LoggerFactory.getLogger(CoreConfig.class);
/** Key for {@link Config#get(SectionParser)}. */
public static final Config.SectionParser<CoreConfig> KEY = CoreConfig::new;
@@ -127,7 +131,9 @@ public class CoreConfig {
* Permissible values for {@code core.trustPackedRefsStat}.
*
* @since 6.1.1
+ * @deprecated use {@link TrustStat} instead
*/
+ @Deprecated(since = "7.2", forRemoval = true)
public enum TrustPackedRefsStat {
/** Do not trust file attributes of the packed-refs file. */
NEVER,
@@ -135,12 +141,15 @@ public class CoreConfig {
/** Trust file attributes of the packed-refs file. */
ALWAYS,
- /** Open and close the packed-refs file to refresh its file attributes
- * and then trust it. */
+ /**
+ * Open and close the packed-refs file to refresh its file attributes
+ * and then trust it.
+ */
AFTER_OPEN,
- /** {@code core.trustPackedRefsStat} defaults to this when it is
- * not set */
+ /**
+ * {@code core.trustPackedRefsStat} defaults to this when it is not set
+ */
UNSET
}
@@ -148,29 +157,66 @@ public class CoreConfig {
* Permissible values for {@code core.trustLooseRefStat}.
*
* @since 6.9
+ * @deprecated use {@link TrustStat} instead
*/
+ @Deprecated(since = "7.2", forRemoval = true)
public enum TrustLooseRefStat {
/** Trust file attributes of the loose ref. */
ALWAYS,
- /** Open and close parent directories of the loose ref file until the
- * repository root to refresh its file attributes and then trust it. */
+ /**
+ * Open and close parent directories of the loose ref file until the
+ * repository root to refresh its file attributes and then trust it.
+ */
AFTER_OPEN,
}
+ /**
+ * Values for {@code core.trustXXX} options.
+ *
+ * @since 7.2
+ */
+ public enum TrustStat {
+ /** Do not trust file attributes of a File. */
+ NEVER,
+
+ /** Always trust file attributes of a File. */
+ ALWAYS,
+
+ /** Open and close the File to refresh its file attributes
+ * and then trust it. */
+ AFTER_OPEN,
+
+ /**
+ * Used for specific options to inherit value from value set for
+ * core.trustStat.
+ */
+ INHERIT
+ }
+
private final int compression;
private final int packIndexVersion;
- private final LogRefUpdates logAllRefUpdates;
-
private final String excludesfile;
private final String attributesfile;
private final boolean commitGraph;
+ private final TrustStat trustStat;
+
+ private final TrustStat trustPackedRefsStat;
+
+ private final TrustStat trustLooseRefStat;
+
+ private final TrustStat trustPackStat;
+
+ private final TrustStat trustLooseObjectStat;
+
+ private final TrustStat trustTablesListStat;
+
/**
* Options for symlink handling
*
@@ -200,14 +246,17 @@ public class CoreConfig {
DOTGITONLY
}
- private CoreConfig(Config rc) {
+ /**
+ * Create a new core configuration from the passed configuration.
+ *
+ * @param rc
+ * git configuration
+ */
+ CoreConfig(Config rc) {
compression = rc.getInt(ConfigConstants.CONFIG_CORE_SECTION,
ConfigConstants.CONFIG_KEY_COMPRESSION, DEFAULT_COMPRESSION);
packIndexVersion = rc.getInt(ConfigConstants.CONFIG_PACK_SECTION,
ConfigConstants.CONFIG_KEY_INDEXVERSION, 2);
- logAllRefUpdates = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES,
- LogRefUpdates.TRUE);
excludesfile = rc.getString(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_EXCLUDESFILE);
attributesfile = rc.getString(ConfigConstants.CONFIG_CORE_SECTION,
@@ -215,6 +264,68 @@ public class CoreConfig {
commitGraph = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
ConfigConstants.CONFIG_COMMIT_GRAPH,
DEFAULT_COMMIT_GRAPH_ENABLE);
+
+ trustStat = parseTrustStat(rc);
+ trustPackedRefsStat = parseTrustPackedRefsStat(rc);
+ trustLooseRefStat = parseTrustLooseRefStat(rc);
+ trustPackStat = parseTrustPackFileStat(rc);
+ trustLooseObjectStat = parseTrustLooseObjectFileStat(rc);
+ trustTablesListStat = parseTablesListStat(rc);
+ }
+
+ private static TrustStat parseTrustStat(Config rc) {
+ Boolean tfs = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+ ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT);
+ TrustStat ts = rc.getEnum(TrustStat.values(),
+ ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_TRUST_STAT);
+ if (tfs != null) {
+ if (ts == null) {
+ LOG.warn(JGitText.get().deprecatedTrustFolderStat);
+ return tfs.booleanValue() ? TrustStat.ALWAYS : TrustStat.NEVER;
+ }
+ LOG.warn(JGitText.get().precedenceTrustConfig);
+ }
+ if (ts == null) {
+ ts = TrustStat.ALWAYS;
+ } else if (ts == TrustStat.INHERIT) {
+ LOG.warn(JGitText.get().invalidTrustStat);
+ ts = TrustStat.ALWAYS;
+ }
+ return ts;
+ }
+
+ private TrustStat parseTrustPackedRefsStat(Config rc) {
+ return inheritParseTrustStat(rc,
+ ConfigConstants.CONFIG_KEY_TRUST_PACKED_REFS_STAT);
+ }
+
+ private TrustStat parseTrustLooseRefStat(Config rc) {
+ return inheritParseTrustStat(rc,
+ ConfigConstants.CONFIG_KEY_TRUST_LOOSE_REF_STAT);
+ }
+
+ private TrustStat parseTrustPackFileStat(Config rc) {
+ return inheritParseTrustStat(rc,
+ ConfigConstants.CONFIG_KEY_TRUST_PACK_STAT);
+ }
+
+ private TrustStat parseTrustLooseObjectFileStat(Config rc) {
+ return inheritParseTrustStat(rc,
+ ConfigConstants.CONFIG_KEY_TRUST_LOOSE_OBJECT_STAT);
+ }
+
+ private TrustStat inheritParseTrustStat(Config rc, String key) {
+ TrustStat t = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, key,
+ TrustStat.INHERIT);
+ return t == TrustStat.INHERIT ? trustStat : t;
+ }
+
+ private TrustStat parseTablesListStat(Config rc) {
+ TrustStat t = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_TRUST_TABLESLIST_STAT,
+ TrustStat.INHERIT);
+ return t == TrustStat.INHERIT ? trustStat : t;
}
/**
@@ -236,20 +347,6 @@ public class CoreConfig {
}
/**
- * Whether to log all refUpdates
- *
- * @return whether to log all refUpdates
- * @deprecated since 5.6; default value depends on whether the repository is
- * bare. Use
- * {@link Config#getEnum(String, String, String, Enum)}
- * directly.
- */
- @Deprecated
- public boolean isLogAllRefUpdates() {
- return !LogRefUpdates.FALSE.equals(logAllRefUpdates);
- }
-
- /**
* Get path of excludesfile
*
* @return path of excludesfile
@@ -279,4 +376,70 @@ public class CoreConfig {
public boolean enableCommitGraph() {
return commitGraph;
}
+
+ /**
+ * Get how far we can trust file attributes of packed-refs file which is
+ * used to store {@link org.eclipse.jgit.lib.Ref}s in
+ * {@link org.eclipse.jgit.internal.storage.file.RefDirectory}.
+ *
+ * @return how far we can trust file attributes of packed-refs file.
+ *
+ * @since 7.2
+ */
+ public TrustStat getTrustPackedRefsStat() {
+ return trustPackedRefsStat;
+ }
+
+ /**
+ * Get how far we can trust file attributes of loose ref files which are
+ * used to store {@link org.eclipse.jgit.lib.Ref}s in
+ * {@link org.eclipse.jgit.internal.storage.file.RefDirectory}.
+ *
+ * @return how far we can trust file attributes of loose ref files.
+ *
+ * @since 7.2
+ */
+ public TrustStat getTrustLooseRefStat() {
+ return trustLooseRefStat;
+ }
+
+ /**
+ * Get how far we can trust file attributes of packed-refs file which is
+ * used to store {@link org.eclipse.jgit.lib.Ref}s in
+ * {@link org.eclipse.jgit.internal.storage.file.RefDirectory}.
+ *
+ * @return how far we can trust file attributes of packed-refs file.
+ *
+ * @since 7.2
+ */
+ public TrustStat getTrustPackStat() {
+ return trustPackStat;
+ }
+
+ /**
+ * Get how far we can trust file attributes of loose ref files which are
+ * used to store {@link org.eclipse.jgit.lib.Ref}s in
+ * {@link org.eclipse.jgit.internal.storage.file.RefDirectory}.
+ *
+ * @return how far we can trust file attributes of loose ref files.
+ *
+ * @since 7.2
+ */
+ public TrustStat getTrustLooseObjectStat() {
+ return trustLooseObjectStat;
+ }
+
+ /**
+ * Get how far we can trust file attributes of the "tables.list" file which
+ * is used to store the list of filenames of the files storing
+ * {@link org.eclipse.jgit.internal.storage.reftable.Reftable}s in
+ * {@link org.eclipse.jgit.internal.storage.file.FileReftableDatabase}.
+ *
+ * @return how far we can trust file attributes of the "tables.list" file.
+ *
+ * @since 7.2
+ */
+ public TrustStat getTrustTablesListStat() {
+ return trustTablesListStat;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
index a71549c92e..3059f283fe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
@@ -18,6 +18,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Config.ConfigEnum;
import org.eclipse.jgit.transport.RefSpec;
@@ -31,27 +32,37 @@ import org.eclipse.jgit.util.StringUtils;
*/
public class DefaultTypedConfigGetter implements TypedConfigGetter {
+ @SuppressWarnings("boxed")
@Override
public boolean getBoolean(Config config, String section, String subsection,
String name, boolean defaultValue) {
+ return neverNull(getBoolean(config, section, subsection, name,
+ Boolean.valueOf(defaultValue)));
+ }
+
+ @Nullable
+ @Override
+ public Boolean getBoolean(Config config, String section, String subsection,
+ String name, @Nullable Boolean defaultValue) {
String n = config.getString(section, subsection, name);
if (n == null) {
return defaultValue;
}
if (Config.isMissing(n)) {
- return true;
+ return Boolean.TRUE;
}
try {
- return StringUtils.toBoolean(n);
+ return Boolean.valueOf(StringUtils.toBoolean(n));
} catch (IllegalArgumentException err) {
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().invalidBooleanValue, section, name, n), err);
}
}
+ @Nullable
@Override
public <T extends Enum<?>> T getEnum(Config config, T[] all, String section,
- String subsection, String name, T defaultValue) {
+ String subsection, String name, @Nullable T defaultValue) {
String value = config.getString(section, subsection, name);
if (value == null) {
return defaultValue;
@@ -107,9 +118,27 @@ public class DefaultTypedConfigGetter implements TypedConfigGetter {
@Override
public int getInt(Config config, String section, String subsection,
String name, int defaultValue) {
- long val = config.getLong(section, subsection, name, defaultValue);
+ return neverNull(getInt(config, section, subsection, name,
+ Integer.valueOf(defaultValue)));
+ }
+
+ @Nullable
+ @Override
+ @SuppressWarnings("boxing")
+ public Integer getInt(Config config, String section, String subsection,
+ String name, @Nullable Integer defaultValue) {
+ Long longDefault = defaultValue != null
+ ? Long.valueOf(defaultValue.longValue())
+ : null;
+ Long val = config.getLong(section, subsection, name);
+ if (val == null) {
+ val = longDefault;
+ }
+ if (val == null) {
+ return null;
+ }
if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE) {
- return (int) val;
+ return Integer.valueOf(Math.toIntExact(val));
}
throw new IllegalArgumentException(MessageFormat
.format(JGitText.get().integerValueOutOfRange, section, name));
@@ -118,37 +147,56 @@ public class DefaultTypedConfigGetter implements TypedConfigGetter {
@Override
public int getIntInRange(Config config, String section, String subsection,
String name, int minValue, int maxValue, int defaultValue) {
- int val = getInt(config, section, subsection, name, defaultValue);
+ return neverNull(getIntInRange(config, section, subsection, name,
+ minValue, maxValue, Integer.valueOf(defaultValue)));
+ }
+
+ @Override
+ @SuppressWarnings("boxing")
+ public Integer getIntInRange(Config config, String section,
+ String subsection, String name, int minValue, int maxValue,
+ Integer defaultValue) {
+ Integer val = getInt(config, section, subsection, name, defaultValue);
+ if (val == null) {
+ return null;
+ }
if ((val >= minValue && val <= maxValue) || val == UNSET_INT) {
return val;
}
if (subsection == null) {
- throw new IllegalArgumentException(MessageFormat.format(
- JGitText.get().integerValueNotInRange, section, name,
- Integer.valueOf(val), Integer.valueOf(minValue),
- Integer.valueOf(maxValue)));
+ throw new IllegalArgumentException(
+ MessageFormat.format(JGitText.get().integerValueNotInRange,
+ section, name, val, minValue, maxValue));
}
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().integerValueNotInRangeSubSection, section,
- subsection, name, Integer.valueOf(val),
- Integer.valueOf(minValue), Integer.valueOf(maxValue)));
+ subsection, name, val, minValue, maxValue));
}
@Override
public long getLong(Config config, String section, String subsection,
String name, long defaultValue) {
- final String str = config.getString(section, subsection, name);
+ return neverNull(getLong(config, section, subsection, name,
+ Long.valueOf(defaultValue)));
+ }
+
+ @Nullable
+ @Override
+ public Long getLong(Config config, String section, String subsection,
+ String name, @Nullable Long defaultValue) {
+ String str = config.getString(section, subsection, name);
if (str == null) {
return defaultValue;
}
try {
- return StringUtils.parseLongWithSuffix(str, false);
+ return Long.valueOf(StringUtils.parseLongWithSuffix(str, false));
} catch (StringIndexOutOfBoundsException e) {
// Empty
return defaultValue;
} catch (NumberFormatException nfe) {
- throw new IllegalArgumentException(MessageFormat.format(
- JGitText.get().invalidIntegerValue, section, name, str),
+ throw new IllegalArgumentException(
+ MessageFormat.format(JGitText.get().invalidIntegerValue,
+ section, name, str),
nfe);
}
}
@@ -156,6 +204,13 @@ public class DefaultTypedConfigGetter implements TypedConfigGetter {
@Override
public long getTimeUnit(Config config, String section, String subsection,
String name, long defaultValue, TimeUnit wantUnit) {
+ return neverNull(getTimeUnit(config, section, subsection, name,
+ Long.valueOf(defaultValue), wantUnit));
+ }
+
+ @Override
+ public Long getTimeUnit(Config config, String section, String subsection,
+ String name, @Nullable Long defaultValue, TimeUnit wantUnit) {
String valueString = config.getString(section, subsection, name);
if (valueString == null) {
@@ -232,8 +287,8 @@ public class DefaultTypedConfigGetter implements TypedConfigGetter {
}
try {
- return wantUnit.convert(Long.parseLong(digits) * inputMul,
- inputUnit);
+ return Long.valueOf(wantUnit
+ .convert(Long.parseLong(digits) * inputMul, inputUnit));
} catch (NumberFormatException nfe) {
IllegalArgumentException iae = notTimeUnit(section, subsection,
unitName, valueString);
@@ -274,4 +329,14 @@ public class DefaultTypedConfigGetter implements TypedConfigGetter {
}
return result;
}
+
+ // Trick for the checkers. When we use this, one is never null, but
+ // they don't know.
+ @NonNull
+ private static <T> T neverNull(T one) {
+ if (one == null) {
+ throw new IllegalArgumentException();
+ }
+ return one;
+ }
}
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 427a235f3b..23d16db39f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
@@ -24,7 +24,13 @@ public class GpgConfig {
/** Value for openpgp */
OPENPGP("openpgp"), //$NON-NLS-1$
/** Value for x509 */
- X509("x509"); //$NON-NLS-1$
+ X509("x509"), //$NON-NLS-1$
+ /**
+ * Value for ssh.
+ *
+ * @since 7.0
+ */
+ SSH("ssh"); //$NON-NLS-1$
private final String configValue;
@@ -55,26 +61,11 @@ public class GpgConfig {
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;
- }
+ private final String sshDefaultKeyCommand;
+
+ private final String sshAllowedSignersFile;
+
+ private final String sshRevocationFile;
/**
* Create a new GPG config that reads the configuration from config.
@@ -83,18 +74,18 @@ public class GpgConfig {
* the config to read from
*/
public GpgConfig(Config config) {
- keyFormat = config.getEnum(GpgFormat.values(),
- ConfigConstants.CONFIG_GPG_SECTION, null,
+ keyFormat = config.getEnum(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) {
+ if (exe == null && GpgFormat.OPENPGP.equals(keyFormat)) {
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);
@@ -102,6 +93,17 @@ public class GpgConfig {
ConfigConstants.CONFIG_KEY_GPGSIGN, false);
forceAnnotated = config.getBoolean(ConfigConstants.CONFIG_TAG_SECTION,
ConfigConstants.CONFIG_KEY_FORCE_SIGN_ANNOTATED, false);
+ sshDefaultKeyCommand = config.getString(
+ ConfigConstants.CONFIG_GPG_SECTION,
+ ConfigConstants.CONFIG_SSH_SUBSECTION,
+ ConfigConstants.CONFIG_KEY_SSH_DEFAULT_KEY_COMMAND);
+ sshAllowedSignersFile = config.getString(
+ ConfigConstants.CONFIG_GPG_SECTION,
+ ConfigConstants.CONFIG_SSH_SUBSECTION,
+ ConfigConstants.CONFIG_KEY_SSH_ALLOWED_SIGNERS_FILE);
+ sshRevocationFile = config.getString(ConfigConstants.CONFIG_GPG_SECTION,
+ ConfigConstants.CONFIG_SSH_SUBSECTION,
+ ConfigConstants.CONFIG_KEY_SSH_REVOCATION_FILE);
}
/**
@@ -165,4 +167,37 @@ public class GpgConfig {
public boolean isSignAnnotated() {
return forceAnnotated;
}
+
+ /**
+ * Retrieves the value of git config {@code gpg.ssh.defaultKeyCommand}.
+ *
+ * @return the value of {@code gpg.ssh.defaultKeyCommand}
+ *
+ * @since 7.1
+ */
+ public String getSshDefaultKeyCommand() {
+ return sshDefaultKeyCommand;
+ }
+
+ /**
+ * Retrieves the value of git config {@code gpg.ssh.allowedSignersFile}.
+ *
+ * @return the value of {@code gpg.ssh.allowedSignersFile}
+ *
+ * @since 7.1
+ */
+ public String getSshAllowedSignersFile() {
+ return sshAllowedSignersFile;
+ }
+
+ /**
+ * Retrieves the value of git config {@code gpg.ssh.revocationFile}.
+ *
+ * @return the value of {@code gpg.ssh.revocationFile}
+ *
+ * @since 7.1
+ */
+ public String getSshRevocationFile() {
+ return sshRevocationFile;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java
deleted file mode 100644
index 074f46567b..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 91c9bab5a4..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.org> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.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 GpgSignatureVerifier} 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 config
- * the {@link GpgConfig}
- * @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
- * @since 6.9
- */
- default SignatureVerification verify(@NonNull GpgConfig config, byte[] data,
- byte[] signatureData) throws IOException {
- // Default implementation for backwards compatibility; override as
- // appropriate
- return verify(data, signatureData);
- }
-
- /**
- * 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
- * @deprecated since 6.9, use {@link #verify(GpgConfig, byte[], byte[])}
- * instead
- */
- @Deprecated
- 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
deleted file mode 100644
index 59775c475b..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2021, 2022 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 class DefaultFactory {
-
- 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;
- }
-
- private DefaultFactory() {
- // No instantiation
- }
-
- public static GpgSignatureVerifierFactory getDefault() {
- return defaultFactory;
- }
-
- /**
- * Sets the default factory.
- *
- * @param factory
- * the new default factory
- */
- public static void setDefault(GpgSignatureVerifierFactory factory) {
- defaultFactory = factory;
- }
- }
-
- /**
- * Retrieves the default factory.
- *
- * @return the default factory or {@code null} if none set
- */
- public static GpgSignatureVerifierFactory getDefault() {
- return DefaultFactory.getDefault();
- }
-
- /**
- * Sets the default factory.
- *
- * @param factory
- * the new default factory
- */
- public static void setDefault(GpgSignatureVerifierFactory factory) {
- DefaultFactory.setDefault(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/GpgSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java
deleted file mode 100644
index b25a61b506..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2018, 2022 Salesforce and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.lib;
-
-import java.util.Iterator;
-import java.util.ServiceConfigurationError;
-import java.util.ServiceLoader;
-
-import org.eclipse.jgit.annotations.NonNull;
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.api.errors.CanceledException;
-import org.eclipse.jgit.transport.CredentialsProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Creates GPG signatures for Git objects.
- *
- * @since 5.3
- */
-public abstract class GpgSigner {
-
- private static final Logger LOG = LoggerFactory.getLogger(GpgSigner.class);
-
- private static class DefaultSigner {
-
- private static volatile GpgSigner defaultSigner = loadGpgSigner();
-
- private static GpgSigner loadGpgSigner() {
- try {
- ServiceLoader<GpgSigner> loader = ServiceLoader
- .load(GpgSigner.class);
- Iterator<GpgSigner> iter = loader.iterator();
- if (iter.hasNext()) {
- return iter.next();
- }
- } catch (ServiceConfigurationError e) {
- LOG.error(e.getMessage(), e);
- }
- return null;
- }
-
- private DefaultSigner() {
- // No instantiation
- }
-
- public static GpgSigner getDefault() {
- return defaultSigner;
- }
-
- public static void setDefault(GpgSigner signer) {
- defaultSigner = signer;
- }
- }
-
- /**
- * Get the default signer, or <code>null</code>.
- *
- * @return the default signer, or <code>null</code>.
- */
- public static GpgSigner getDefault() {
- return DefaultSigner.getDefault();
- }
-
- /**
- * Set the default signer.
- *
- * @param signer
- * the new default signer, may be <code>null</code> to select no
- * default.
- */
- public static void setDefault(GpgSigner signer) {
- DefaultSigner.setDefault(signer);
- }
-
- /**
- * Signs the specified commit.
- *
- * <p>
- * Implementors should obtain the payload for signing from the specified
- * commit via {@link CommitBuilder#build()} and create a proper
- * {@link GpgSignature}. The generated signature must be set on the
- * specified {@code commit} (see
- * {@link CommitBuilder#setGpgSignature(GpgSignature)}).
- * </p>
- * <p>
- * Any existing signature on the commit must be discarded prior obtaining
- * the payload via {@link CommitBuilder#build()}.
- * </p>
- *
- * @param commit
- * the commit to sign (must not be <code>null</code> and must be
- * complete to allow proper calculation of payload)
- * @param gpgSigningKey
- * the signing key to locate (passed as is to the GPG signing
- * tool as is; eg., value of <code>user.signingkey</code>)
- * @param committer
- * the signing identity (to help with key lookup in case signing
- * key is not specified)
- * @param credentialsProvider
- * provider to use when querying for signing key credentials (eg.
- * passphrase)
- * @throws CanceledException
- * when signing was canceled (eg., user aborted when entering
- * passphrase)
- */
- public abstract void sign(@NonNull CommitBuilder commit,
- @Nullable String gpgSigningKey, @NonNull PersonIdent committer,
- CredentialsProvider credentialsProvider) throws CanceledException;
-
- /**
- * Indicates if a signing key is available for the specified committer
- * and/or signing key.
- *
- * @param gpgSigningKey
- * the signing key to locate (passed as is to the GPG signing
- * tool as is; eg., value of <code>user.signingkey</code>)
- * @param committer
- * the signing identity (to help with key lookup in case signing
- * key is not specified)
- * @param credentialsProvider
- * provider to use when querying for signing key credentials (eg.
- * passphrase)
- * @return <code>true</code> if a signing key is available,
- * <code>false</code> otherwise
- * @throws CanceledException
- * when signing was canceled (eg., user aborted when entering
- * passphrase)
- */
- public abstract boolean canLocateSigningKey(@Nullable String gpgSigningKey,
- @NonNull PersonIdent committer,
- CredentialsProvider credentialsProvider) throws CanceledException;
-
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
index 8e965c5e9d..a99c64701f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -639,7 +639,7 @@ public class IndexDiff {
// submodule repository in .git/modules doesn't
// exist yet it isn't "missing".
File gitDir = new File(
- new File(repository.getDirectory(),
+ new File(repository.getCommonDirectory(),
Constants.MODULES),
subRepoPath);
if (!gitDir.isDirectory()) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
index 1c31263e33..1b455b974d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
@@ -15,6 +15,7 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.nio.ByteBuffer;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.InvalidObjectIdException;
@@ -152,6 +153,22 @@ public class ObjectId extends AnyObjectId implements Serializable {
}
/**
+ * Convert an ObjectId from raw binary representation
+ *
+ * @param bb
+ * a bytebuffer with the objectid encoded as 5 consecutive ints.
+ * This is the reverse of {@link ObjectId#copyRawTo(ByteBuffer)}
+ *
+ * @return the converted object id.
+ *
+ * @since 7.0
+ */
+ public static final ObjectId fromRaw(ByteBuffer bb) {
+ return new ObjectId(bb.getInt(), bb.getInt(), bb.getInt(), bb.getInt(),
+ bb.getInt());
+ }
+
+ /**
* Convert an ObjectId from raw binary representation.
*
* @param is
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
index 3ba055aae8..50f4a83b93 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
@@ -12,10 +12,15 @@
package org.eclipse.jgit.lib;
+import static java.time.ZoneOffset.UTC;
+
import java.io.Serializable;
-import java.text.SimpleDateFormat;
+import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@@ -40,7 +45,9 @@ public class PersonIdent implements Serializable {
* timezone offset as in {@link #getTimeZoneOffset()}.
* @return time zone object for the given offset.
* @since 4.1
+ * @deprecated use {@link #getZoneId(int)} instead
*/
+ @Deprecated(since = "7.2")
public static TimeZone getTimeZone(int tzOffset) {
StringBuilder tzId = new StringBuilder(8);
tzId.append("GMT"); //$NON-NLS-1$
@@ -49,6 +56,21 @@ public class PersonIdent implements Serializable {
}
/**
+ * Translate a minutes offset into a ZoneId
+ *
+ * @param tzOffset as minutes east of UTC
+ * @return a ZoneId for this offset (UTC if invalid)
+ * @since 7.1
+ */
+ public static ZoneId getZoneId(int tzOffset) {
+ try {
+ return ZoneOffset.ofHoursMinutes(tzOffset / 60, tzOffset % 60);
+ } catch (DateTimeException e) {
+ return UTC;
+ }
+ }
+
+ /**
* Format a timezone offset.
*
* @param r
@@ -121,13 +143,17 @@ public class PersonIdent implements Serializable {
}
}
+ // Write offsets as [+-]HHMM
+ private static final DateTimeFormatter OFFSET_FORMATTER = DateTimeFormatter
+ .ofPattern("Z", Locale.US); //$NON-NLS-1$
+
private final String name;
private final String emailAddress;
- private final long when;
+ private final Instant when;
- private final int tzOffset;
+ private final ZoneId tzOffset;
/**
* Creates new PersonIdent from config info in repository, with current time.
@@ -160,7 +186,7 @@ public class PersonIdent implements Serializable {
* a {@link java.lang.String} object.
*/
public PersonIdent(String aName, String aEmailAddress) {
- this(aName, aEmailAddress, SystemReader.getInstance().getCurrentTime());
+ this(aName, aEmailAddress, SystemReader.getInstance().now());
}
/**
@@ -177,7 +203,7 @@ public class PersonIdent implements Serializable {
*/
public PersonIdent(String aName, String aEmailAddress,
ProposedTimestamp when) {
- this(aName, aEmailAddress, when.millis());
+ this(aName, aEmailAddress, when.instant());
}
/**
@@ -189,8 +215,25 @@ public class PersonIdent implements Serializable {
* local time
* @param tz
* time zone
+ * @deprecated Use {@link #PersonIdent(PersonIdent, Instant, ZoneId)} instead.
*/
+ @Deprecated(since = "7.1")
public PersonIdent(PersonIdent pi, Date when, TimeZone tz) {
+ this(pi.getName(), pi.getEmailAddress(), when.toInstant(), tz.toZoneId());
+ }
+
+ /**
+ * Copy a PersonIdent, but alter the clone's time stamp
+ *
+ * @param pi
+ * original {@link org.eclipse.jgit.lib.PersonIdent}
+ * @param when
+ * local time
+ * @param tz
+ * time zone offset
+ * @since 7.1
+ */
+ public PersonIdent(PersonIdent pi, Instant when, ZoneId tz) {
this(pi.getName(), pi.getEmailAddress(), when, tz);
}
@@ -202,9 +245,12 @@ public class PersonIdent implements Serializable {
* original {@link org.eclipse.jgit.lib.PersonIdent}
* @param aWhen
* local time
+ * @deprecated Use the variant with an Instant instead
*/
+ @Deprecated(since = "7.1")
public PersonIdent(PersonIdent pi, Date aWhen) {
- this(pi.getName(), pi.getEmailAddress(), aWhen.getTime(), pi.tzOffset);
+ this(pi.getName(), pi.getEmailAddress(), aWhen.toInstant(),
+ pi.tzOffset);
}
/**
@@ -218,7 +264,7 @@ public class PersonIdent implements Serializable {
* @since 6.1
*/
public PersonIdent(PersonIdent pi, Instant aWhen) {
- this(pi.getName(), pi.getEmailAddress(), aWhen.toEpochMilli(), pi.tzOffset);
+ this(pi.getName(), pi.getEmailAddress(), aWhen, pi.tzOffset);
}
/**
@@ -230,11 +276,12 @@ public class PersonIdent implements Serializable {
* local time stamp
* @param aTZ
* time zone
+ * @deprecated Use the variant with Instant and ZoneId instead
*/
+ @Deprecated(since = "7.1")
public PersonIdent(final String aName, final String aEmailAddress,
final Date aWhen, final TimeZone aTZ) {
- this(aName, aEmailAddress, aWhen.getTime(), aTZ.getOffset(aWhen
- .getTime()) / (60 * 1000));
+ this(aName, aEmailAddress, aWhen.toInstant(), aTZ.toZoneId());
}
/**
@@ -252,10 +299,16 @@ public class PersonIdent implements Serializable {
*/
public PersonIdent(final String aName, String aEmailAddress, Instant aWhen,
ZoneId zoneId) {
- this(aName, aEmailAddress, aWhen.toEpochMilli(),
- TimeZone.getTimeZone(zoneId)
- .getOffset(aWhen
- .toEpochMilli()) / (60 * 1000));
+ if (aName == null)
+ throw new IllegalArgumentException(
+ JGitText.get().personIdentNameNonNull);
+ if (aEmailAddress == null)
+ throw new IllegalArgumentException(
+ JGitText.get().personIdentEmailNonNull);
+ name = aName;
+ emailAddress = aEmailAddress;
+ when = aWhen;
+ tzOffset = zoneId;
}
/**
@@ -267,15 +320,18 @@ public class PersonIdent implements Serializable {
* local time stamp
* @param aTZ
* time zone
+ * @deprecated Use the variant with Instant and ZoneId instead
*/
+ @Deprecated(since = "7.1")
public PersonIdent(PersonIdent pi, long aWhen, int aTZ) {
- this(pi.getName(), pi.getEmailAddress(), aWhen, aTZ);
+ this(pi.getName(), pi.getEmailAddress(), Instant.ofEpochMilli(aWhen),
+ getZoneId(aTZ));
}
private PersonIdent(final String aName, final String aEmailAddress,
- long when) {
+ Instant when) {
this(aName, aEmailAddress, when, SystemReader.getInstance()
- .getTimezone(when));
+ .getTimeZoneAt(when));
}
private PersonIdent(UserConfig config) {
@@ -298,19 +354,12 @@ public class PersonIdent implements Serializable {
* local time stamp
* @param aTZ
* time zone
+ * @deprecated Use the variant with Instant and ZoneId instead
*/
+ @Deprecated(since = "7.1")
public PersonIdent(final String aName, final String aEmailAddress,
final long aWhen, final int aTZ) {
- if (aName == null)
- throw new IllegalArgumentException(
- JGitText.get().personIdentNameNonNull);
- if (aEmailAddress == null)
- throw new IllegalArgumentException(
- JGitText.get().personIdentEmailNonNull);
- name = aName;
- emailAddress = aEmailAddress;
- when = aWhen;
- tzOffset = aTZ;
+ this(aName, aEmailAddress, Instant.ofEpochMilli(aWhen), getZoneId(aTZ));
}
/**
@@ -335,9 +384,12 @@ public class PersonIdent implements Serializable {
* Get timestamp
*
* @return timestamp
+ *
+ * @deprecated Use getWhenAsInstant instead
*/
+ @Deprecated(since = "7.1")
public Date getWhen() {
- return new Date(when);
+ return Date.from(when);
}
/**
@@ -347,16 +399,19 @@ public class PersonIdent implements Serializable {
* @since 6.1
*/
public Instant getWhenAsInstant() {
- return Instant.ofEpochMilli(when);
+ return when;
}
/**
* Get this person's declared time zone
*
* @return this person's declared time zone; null if time zone is unknown.
+ *
+ * @deprecated Use getZoneId instead
*/
+ @Deprecated(since = "7.1")
public TimeZone getTimeZone() {
- return getTimeZone(tzOffset);
+ return TimeZone.getTimeZone(tzOffset);
}
/**
@@ -366,7 +421,17 @@ public class PersonIdent implements Serializable {
* @since 6.1
*/
public ZoneId getZoneId() {
- return getTimeZone().toZoneId();
+ return tzOffset;
+ }
+
+ /**
+ * Return the offset in this timezone at the specific time
+ *
+ * @return the offset
+ * @since 7.1
+ */
+ public ZoneOffset getZoneOffset() {
+ return tzOffset.getRules().getOffset(when);
}
/**
@@ -374,9 +439,11 @@ public class PersonIdent implements Serializable {
*
* @return this person's declared time zone as minutes east of UTC. If the
* timezone is to the west of UTC it is negative.
+ * @deprecated Use {@link #getZoneOffset()} and read minutes from there
*/
+ @Deprecated(since = "7.1")
public int getTimeZoneOffset() {
- return tzOffset;
+ return getZoneOffset().getTotalSeconds() / 60;
}
/**
@@ -388,7 +455,7 @@ public class PersonIdent implements Serializable {
public int hashCode() {
int hc = getEmailAddress().hashCode();
hc *= 31;
- hc += (int) (when / 1000L);
+ hc += when.hashCode();
return hc;
}
@@ -398,7 +465,9 @@ public class PersonIdent implements Serializable {
final PersonIdent p = (PersonIdent) o;
return getName().equals(p.getName())
&& getEmailAddress().equals(p.getEmailAddress())
- && when / 1000L == p.when / 1000L;
+ // commmit timestamps are stored with 1 second precision
+ && when.truncatedTo(ChronoUnit.SECONDS)
+ .equals(p.when.truncatedTo(ChronoUnit.SECONDS));
}
return false;
}
@@ -414,9 +483,9 @@ public class PersonIdent implements Serializable {
r.append(" <"); //$NON-NLS-1$
appendSanitized(r, getEmailAddress());
r.append("> "); //$NON-NLS-1$
- r.append(when / 1000);
+ r.append(when.toEpochMilli() / 1000);
r.append(' ');
- appendTimezone(r, tzOffset);
+ r.append(OFFSET_FORMATTER.format(getZoneOffset()));
return r.toString();
}
@@ -424,19 +493,16 @@ public class PersonIdent implements Serializable {
@SuppressWarnings("nls")
public String toString() {
final StringBuilder r = new StringBuilder();
- final SimpleDateFormat dtfmt;
- dtfmt = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy Z", Locale.US);
- dtfmt.setTimeZone(getTimeZone());
-
+ DateTimeFormatter dtfmt = DateTimeFormatter
+ .ofPattern("EEE MMM d HH:mm:ss yyyy Z", Locale.US) //$NON-NLS-1$
+ .withZone(tzOffset);
r.append("PersonIdent[");
r.append(getName());
r.append(", ");
r.append(getEmailAddress());
r.append(", ");
- r.append(dtfmt.format(Long.valueOf(when)));
+ r.append(dtfmt.format(when));
r.append("]");
-
return r.toString();
}
}
-
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 9e05a39731..49d5224325 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -26,6 +26,7 @@ import java.util.stream.Stream;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.PackRefsCommand;
/**
* Abstraction of name to {@link org.eclipse.jgit.lib.ObjectId} mapping.
@@ -160,7 +161,7 @@ public abstract class RefDatabase {
if (existing.startsWith(prefix))
conflicting.add(existing);
- return conflicting;
+ return Collections.unmodifiableList(conflicting);
}
/**
@@ -238,23 +239,6 @@ public abstract class RefDatabase {
}
/**
- * Compatibility synonym for {@link #findRef(String)}.
- *
- * @param name
- * the name of the reference. May be a short name which must be
- * searched for using the standard {@link #SEARCH_PATH}.
- * @return the reference (if it exists); else {@code null}.
- * @throws IOException
- * the reference space cannot be accessed.
- * @deprecated Use {@link #findRef(String)} instead.
- */
- @Deprecated
- @Nullable
- public final Ref getRef(String name) throws IOException {
- return findRef(name);
- }
-
- /**
* Read a single reference.
* <p>
* Aside from taking advantage of {@link #SEARCH_PATH}, this method may be
@@ -372,6 +356,40 @@ public abstract class RefDatabase {
}
/**
+ * Get the reflog reader
+ *
+ * @param refName
+ * a {@link java.lang.String} object.
+ * @return a {@link org.eclipse.jgit.lib.ReflogReader} for the supplied
+ * refname, or {@code null} if the named ref does not exist.
+ * @throws java.io.IOException
+ * the ref could not be accessed.
+ * @since 7.2
+ */
+ @Nullable
+ public ReflogReader getReflogReader(String refName) throws IOException {
+ Ref ref = exactRef(refName);
+ if (ref == null) {
+ return null;
+ }
+ return getReflogReader(ref);
+ }
+
+ /**
+ * Get the reflog reader.
+ *
+ * @param ref
+ * a Ref
+ * @return a {@link org.eclipse.jgit.lib.ReflogReader} for the supplied ref.
+ * @throws IOException
+ * if an IO error occurred
+ * @since 7.2
+ */
+ @NonNull
+ public abstract ReflogReader getReflogReader(@NonNull Ref ref)
+ throws IOException;
+
+ /**
* Get a section of the reference namespace.
*
* @param prefix
@@ -610,4 +628,22 @@ public abstract class RefDatabase {
}
return null;
}
+
+ /**
+ * Optimize pack ref storage.
+ *
+ * @param pm
+ * a progress monitor
+ *
+ * @param packRefs
+ * {@link PackRefsCommand} to control ref packing behavior
+ *
+ * @throws java.io.IOException
+ * if an IO error occurred
+ * @since 7.1
+ */
+ public void packRefs(ProgressMonitor pm, PackRefsCommand packRefs)
+ throws IOException {
+ // nothing
+ }
}
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 4722e29b4c..c9dc6da4ba 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -26,6 +26,8 @@ import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
@@ -33,10 +35,12 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import org.eclipse.jgit.annotations.NonNull;
@@ -113,9 +117,12 @@ public abstract class Repository implements AutoCloseable {
final AtomicLong closedAt = new AtomicLong();
- /** Metadata directory holding the repository's critical files. */
+ /** $GIT_DIR: metadata directory holding the repository's critical files. */
private final File gitDir;
+ /** $GIT_COMMON_DIR: metadata directory holding the common repository's critical files. */
+ private final File gitCommonDir;
+
/** File abstraction used to resolve paths. */
private final FS fs;
@@ -129,6 +136,8 @@ public abstract class Repository implements AutoCloseable {
private final String initialBranch;
+ private final AtomicReference<Boolean> caseInsensitiveWorktree = new AtomicReference<>();
+
/**
* Initialize a new repository instance.
*
@@ -137,6 +146,7 @@ public abstract class Repository implements AutoCloseable {
*/
protected Repository(BaseRepositoryBuilder options) {
gitDir = options.getGitDir();
+ gitCommonDir = options.getGitCommonDir();
fs = options.getFS();
workTree = options.getWorkTree();
indexFile = options.getIndexFile();
@@ -220,6 +230,16 @@ public abstract class Repository implements AutoCloseable {
public abstract String getIdentifier();
/**
+ * Get common dir.
+ *
+ * @return $GIT_COMMON_DIR: local common metadata directory;
+ * @since 7.0
+ */
+ public File getCommonDirectory() {
+ return gitCommonDir;
+ }
+
+ /**
* Get the object database which stores this repository's data.
*
* @return the object database which stores this repository's data.
@@ -293,25 +313,6 @@ public abstract class Repository implements AutoCloseable {
}
/**
- * Whether the specified object is stored in this repo or any of the known
- * shared repositories.
- *
- * @param objectId
- * a {@link org.eclipse.jgit.lib.AnyObjectId} object.
- * @return true if the specified object is stored in this repo or any of the
- * known shared repositories.
- * @deprecated use {@code getObjectDatabase().has(objectId)}
- */
- @Deprecated
- public boolean hasObject(AnyObjectId objectId) {
- try {
- return getObjectDatabase().has(objectId);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- /**
* Open an object from this repository.
* <p>
* This is a one-shot call interface which may be faster than allocating a
@@ -1150,11 +1151,9 @@ public abstract class Repository implements AutoCloseable {
* new Ref object representing the same data as Ref, but isPeeled()
* will be true and getPeeledObjectId will contain the peeled object
* (or null).
- * @deprecated use {@code getRefDatabase().peel(ref)} instead.
*/
- @Deprecated
@NonNull
- public Ref peel(Ref ref) {
+ private Ref peel(Ref ref) {
try {
return getRefDatabase().peel(ref);
} catch (IOException e) {
@@ -1584,6 +1583,40 @@ public abstract class Repository implements AutoCloseable {
}
/**
+ * Tells whether the work tree is on a case-insensitive file system.
+ *
+ * @return {@code true} if the work tree is case-insensitive; {@code false}
+ * otherwise
+ * @throws NoWorkTreeException
+ * if the repository is bare
+ * @since 7.2
+ */
+ public boolean isWorkTreeCaseInsensitive() throws NoWorkTreeException {
+ Boolean flag = caseInsensitiveWorktree.get();
+ if (flag == null) {
+ File directory = getWorkTree();
+ // See if we can find ".git" also as ".GIT".
+ File dotGit = new File(directory, Constants.DOT_GIT);
+ if (Files.exists(dotGit.toPath(), LinkOption.NOFOLLOW_LINKS)) {
+ dotGit = new File(directory,
+ Constants.DOT_GIT.toUpperCase(Locale.ROOT));
+ flag = Boolean.valueOf(Files.exists(dotGit.toPath(),
+ LinkOption.NOFOLLOW_LINKS));
+ } else {
+ // Fall back to a mostly sane default. On Mac, HFS+ and APFS
+ // partitions are case-insensitive by default but can be
+ // configured to be case-sensitive.
+ SystemReader system = SystemReader.getInstance();
+ flag = Boolean.valueOf(system.isWindows() || system.isMacOS());
+ }
+ if (!caseInsensitiveWorktree.compareAndSet(null, flag)) {
+ flag = caseInsensitiveWorktree.get();
+ }
+ }
+ return flag.booleanValue();
+ }
+
+ /**
* Force a scan for changed refs. Fires an IndexChangedEvent(false) if
* changes are detected.
*
@@ -1699,10 +1732,13 @@ public abstract class Repository implements AutoCloseable {
* @throws java.io.IOException
* the ref could not be accessed.
* @since 3.0
+ * @deprecated use {@code #getRefDatabase().getReflogReader(String)} instead
*/
+ @Deprecated(since = "7.2")
@Nullable
- public abstract ReflogReader getReflogReader(String refName)
- throws IOException;
+ public ReflogReader getReflogReader(String refName) throws IOException {
+ return getRefDatabase().getReflogReader(refName);
+ }
/**
* Get the reflog reader. Subclasses should override this method and provide
@@ -1710,15 +1746,17 @@ public abstract class Repository implements AutoCloseable {
*
* @param ref
* a Ref
- * @return a {@link org.eclipse.jgit.lib.ReflogReader} for the supplied ref,
- * or {@code null} if the ref does not exist.
+ * @return a {@link org.eclipse.jgit.lib.ReflogReader} for the supplied ref.
* @throws IOException
* if an IO error occurred
* @since 5.13.2
+ * @deprecated use {@code #getRefDatabase().getReflogReader(Ref)} instead
*/
- public @Nullable ReflogReader getReflogReader(@NonNull Ref ref)
+ @Deprecated(since = "7.2")
+ @NonNull
+ public ReflogReader getReflogReader(@NonNull Ref ref)
throws IOException {
- return getReflogReader(ref.getName());
+ return getRefDatabase().getReflogReader(ref);
}
/**
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 6288447a8d..18366541da 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -450,10 +450,21 @@ public class RepositoryCache {
* Git directory.
*/
public static boolean isGitRepository(File dir, FS fs) {
- return fs.resolve(dir, Constants.OBJECTS).exists()
- && fs.resolve(dir, "refs").exists() //$NON-NLS-1$
- && (fs.resolve(dir, Constants.REFTABLE).exists()
- || isValidHead(new File(dir, Constants.HEAD)));
+ // check if common-dir available or fallback to git-dir
+ File commonDir;
+ try {
+ commonDir = fs.getCommonDir(dir);
+ } catch (IOException e) {
+ commonDir = null;
+ }
+ if (commonDir == null) {
+ commonDir = dir;
+ }
+ return fs.resolve(commonDir, Constants.OBJECTS).exists()
+ && fs.resolve(commonDir, "refs").exists() //$NON-NLS-1$
+ && (fs.resolve(commonDir, Constants.REFTABLE).exists()
+ || isValidHead(
+ new File(commonDir, Constants.HEAD)));
}
private static boolean isValidHead(File head) {
@@ -496,15 +507,31 @@ public class RepositoryCache {
* null if there is no suitable match.
*/
public static File resolve(File directory, FS fs) {
- if (isGitRepository(directory, fs))
+ // the folder itself
+ if (isGitRepository(directory, fs)) {
return directory;
- if (isGitRepository(new File(directory, Constants.DOT_GIT), fs))
- return new File(directory, Constants.DOT_GIT);
-
- final String name = directory.getName();
- final File parent = directory.getParentFile();
- if (isGitRepository(new File(parent, name + Constants.DOT_GIT_EXT), fs))
- return new File(parent, name + Constants.DOT_GIT_EXT);
+ }
+ // the .git subfolder or file (reference)
+ File dotDir = new File(directory, Constants.DOT_GIT);
+ if (dotDir.isFile()) {
+ try {
+ File refDir = BaseRepositoryBuilder.getSymRef(directory,
+ dotDir, fs);
+ if (refDir != null && isGitRepository(refDir, fs)) {
+ return refDir;
+ }
+ } catch (IOException ignored) {
+ // Continue searching if gitdir ref isn't found
+ }
+ } else if (isGitRepository(dotDir, fs)) {
+ return dotDir;
+ }
+ // the folder extended with .git (bare)
+ File bareDir = new File(directory.getParentFile(),
+ directory.getName() + Constants.DOT_GIT_EXT);
+ if (isGitRepository(bareDir, fs)) {
+ return bareDir;
+ }
return null;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifier.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifier.java
new file mode 100644
index 0000000000..2ce2708cb9
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifier.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+import java.util.Date;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+
+/**
+ * A {@code SignatureVerifier} can verify signatures on git commits and tags.
+ *
+ * @since 7.0
+ */
+public interface SignatureVerifier {
+
+ /**
+ * Verifies a given signature for given data.
+ *
+ * @param repository
+ * the {@link Repository} the data comes from.
+ * @param config
+ * the {@link GpgConfig}
+ * @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
+ */
+ SignatureVerification verify(@NonNull Repository repository,
+ @NonNull GpgConfig config, 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 SignatureVerifier} 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.
+ *
+ * @param verifierName
+ * the name of the verifier that created this verification result
+ * @param creationDate
+ * date and time the signature was created
+ * @param signer
+ * the signer as stored in the signature, or {@code null} if
+ * unknown
+ * @param keyFingerprint
+ * fingerprint of the public key, or {@code null} if unknown
+ * @param keyUser
+ * user associated with the key, or {@code null} if unknown
+ * @param verified
+ * whether the signature verification was successful
+ * @param expired
+ * whether the public key used for this signature verification
+ * was expired when the signature was created
+ * @param trustLevel
+ * the trust level of the public key used to verify the signature
+ * @param message
+ * human-readable message giving additional information about the
+ * outcome of the verification, possibly {@code null}
+ */
+ record SignatureVerification(
+ String verifierName,
+ Date creationDate,
+ String signer,
+ String keyFingerprint,
+ String keyUser,
+ boolean verified,
+ boolean expired,
+ @NonNull TrustLevel trustLevel,
+ String message) {
+ }
+
+ /**
+ * 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/SignatureVerifierFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifierFactory.java
new file mode 100644
index 0000000000..7844aba3bd
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifierFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * A factory for {@link SignatureVerifier}s.
+ *
+ * @since 7.0
+ */
+public interface SignatureVerifierFactory {
+
+ /**
+ * Tells what kind of {@link SignatureVerifier} this factory creates.
+ *
+ * @return the {@link GpgConfig.GpgFormat} of the signer
+ */
+ @NonNull
+ GpgConfig.GpgFormat getType();
+
+ /**
+ * Creates a new instance of a {@link SignatureVerifier} that can produce
+ * signatures of type {@link #getType()}.
+ *
+ * @return a new {@link SignatureVerifier}
+ */
+ @NonNull
+ SignatureVerifier create();
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifiers.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifiers.java
new file mode 100644
index 0000000000..01c8422b66
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifiers.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Manages the available signers.
+ *
+ * @since 7.0
+ */
+public final class SignatureVerifiers {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SignatureVerifiers.class);
+
+ private static final byte[] PGP_PREFIX = Constants.GPG_SIGNATURE_PREFIX
+ .getBytes(StandardCharsets.US_ASCII);
+
+ private static final byte[] X509_PREFIX = Constants.CMS_SIGNATURE_PREFIX
+ .getBytes(StandardCharsets.US_ASCII);
+
+ private static final byte[] SSH_PREFIX = Constants.SSH_SIGNATURE_PREFIX
+ .getBytes(StandardCharsets.US_ASCII);
+
+ private static final Map<GpgConfig.GpgFormat, SignatureVerifierFactory> FACTORIES = loadSignatureVerifiers();
+
+ private static final Map<GpgConfig.GpgFormat, SignatureVerifier> VERIFIERS = new ConcurrentHashMap<>();
+
+ private static Map<GpgConfig.GpgFormat, SignatureVerifierFactory> loadSignatureVerifiers() {
+ Map<GpgConfig.GpgFormat, SignatureVerifierFactory> result = new EnumMap<>(
+ GpgConfig.GpgFormat.class);
+ try {
+ for (SignatureVerifierFactory factory : ServiceLoader
+ .load(SignatureVerifierFactory.class)) {
+ GpgConfig.GpgFormat format = factory.getType();
+ SignatureVerifierFactory existing = result.get(format);
+ if (existing != null) {
+ LOG.warn("{}", //$NON-NLS-1$
+ MessageFormat.format(
+ JGitText.get().signatureServiceConflict,
+ "SignatureVerifierFactory", format, //$NON-NLS-1$
+ existing.getClass().getCanonicalName(),
+ factory.getClass().getCanonicalName()));
+ } else {
+ result.put(format, factory);
+ }
+ }
+ } catch (ServiceConfigurationError e) {
+ LOG.error(e.getMessage(), e);
+ }
+ return result;
+ }
+
+ private SignatureVerifiers() {
+ // No instantiation
+ }
+
+ /**
+ * Retrieves a {@link Signer} that can produce signatures of the given type
+ * {@code format}.
+ *
+ * @param format
+ * {@link GpgConfig.GpgFormat} the signer must support
+ * @return a {@link Signer}, or {@code null} if none is available
+ */
+ public static SignatureVerifier get(@NonNull GpgConfig.GpgFormat format) {
+ return VERIFIERS.computeIfAbsent(format, f -> {
+ SignatureVerifierFactory factory = FACTORIES.get(format);
+ if (factory == null) {
+ return null;
+ }
+ return factory.create();
+ });
+ }
+
+ /**
+ * Sets a specific signature verifier to use for a specific signature type.
+ *
+ * @param format
+ * signature type to set the {@code verifier} for
+ * @param verifier
+ * the {@link SignatureVerifier} to use for signatures of type
+ * {@code format}; if {@code null}, a default implementation, if
+ * available, may be used.
+ */
+ public static void set(@NonNull GpgConfig.GpgFormat format,
+ SignatureVerifier verifier) {
+ SignatureVerifier previous;
+ if (verifier == null) {
+ previous = VERIFIERS.remove(format);
+ } else {
+ previous = VERIFIERS.put(format, verifier);
+ }
+ if (previous != null) {
+ previous.clear();
+ }
+ }
+
+ /**
+ * Verifies the signature on a signed commit or tag.
+ *
+ * @param repository
+ * the {@link Repository} the object is from
+ * @param config
+ * the {@link GpgConfig} to use
+ * @param object
+ * to verify
+ * @return a {@link SignatureVerifier.SignatureVerification} describing the
+ * outcome of the verification, or {@code null} if the object does
+ * not have a signature of a known type
+ * @throws IOException
+ * if an error occurs getting a public key
+ * @throws org.eclipse.jgit.api.errors.JGitInternalException
+ * if signature verification fails
+ */
+ @Nullable
+ public static SignatureVerifier.SignatureVerification verify(
+ @NonNull Repository repository, @NonNull GpgConfig config,
+ @NonNull RevObject object) 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.nextLfSkippingSplitLines(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(repository, config, 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(repository, config, data, signatureData);
+ }
+ return null;
+ }
+
+ /**
+ * Verifies a given signature for some give data.
+ *
+ * @param repository
+ * the {@link Repository} the object is from
+ * @param config
+ * the {@link GpgConfig} to use
+ * @param data
+ * to verify the signature of
+ * @param signature
+ * the given signature of the {@code data}
+ * @return a {@link SignatureVerifier.SignatureVerification} describing the
+ * outcome of the verification, or {@code null} if the signature
+ * type is unknown
+ * @throws IOException
+ * if an error occurs getting a public key
+ * @throws org.eclipse.jgit.api.errors.JGitInternalException
+ * if signature verification fails
+ */
+ @Nullable
+ public static SignatureVerifier.SignatureVerification verify(
+ @NonNull Repository repository, @NonNull GpgConfig config,
+ byte[] data, byte[] signature) throws IOException {
+ GpgConfig.GpgFormat format = getFormat(signature);
+ if (format == null) {
+ return null;
+ }
+ SignatureVerifier verifier = get(format);
+ if (verifier == null) {
+ return null;
+ }
+ return verifier.verify(repository, config, data, signature);
+ }
+
+ /**
+ * Determines the type of a given signature.
+ *
+ * @param signature
+ * to get the type of
+ * @return the signature type, or {@code null} if unknown
+ */
+ @Nullable
+ public static GpgConfig.GpgFormat getFormat(byte[] signature) {
+ if (RawParseUtils.match(signature, 0, PGP_PREFIX) > 0) {
+ return GpgConfig.GpgFormat.OPENPGP;
+ }
+ if (RawParseUtils.match(signature, 0, X509_PREFIX) > 0) {
+ return GpgConfig.GpgFormat.X509;
+ }
+ if (RawParseUtils.match(signature, 0, SSH_PREFIX) > 0) {
+ return GpgConfig.GpgFormat.SSH;
+ }
+ return null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Signer.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Signer.java
new file mode 100644
index 0000000000..3bb7464d08
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Signer.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.jgit.annotations.NonNull;
+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.transport.CredentialsProvider;
+
+/**
+ * Creates signatures for Git objects.
+ *
+ * @since 7.0
+ */
+public interface Signer {
+
+ /**
+ * 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 is 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 repository
+ * {@link Repository} the object belongs to
+ * @param config
+ * GPG settings from the git config
+ * @param object
+ * the object to sign (must not be {@code null} and must be
+ * complete to allow proper calculation of payload)
+ * @param committer
+ * the signing identity (to help with key lookup in case signing
+ * key is not specified)
+ * @param signingKey
+ * if non-{@code null} overrides the signing key from the config
+ * @param credentialsProvider
+ * provider to use when querying for signing key credentials (eg.
+ * passphrase)
+ * @throws CanceledException
+ * when signing was canceled (eg., user aborted when entering
+ * passphrase)
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws UnsupportedSigningFormatException
+ * if a config is given and the wanted key format is not
+ * supported
+ */
+ default void signObject(@NonNull Repository repository,
+ @NonNull GpgConfig config, @NonNull ObjectBuilder object,
+ @NonNull PersonIdent committer, String signingKey,
+ CredentialsProvider credentialsProvider)
+ throws CanceledException, IOException,
+ UnsupportedSigningFormatException {
+ try {
+ object.setGpgSignature(sign(repository, config, object.build(),
+ committer, signingKey, credentialsProvider));
+ } catch (UnsupportedEncodingException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Signs arbitrary data.
+ *
+ * @param repository
+ * {@link Repository} the signature is created in
+ * @param config
+ * GPG settings from the git config
+ * @param data
+ * the data to sign
+ * @param committer
+ * the signing identity (to help with key lookup in case signing
+ * key is not specified)
+ * @param signingKey
+ * if non-{@code null} overrides the signing key from the config
+ * @param credentialsProvider
+ * provider to use when querying for signing key credentials (eg.
+ * passphrase)
+ * @return the signature for {@code data}
+ * @throws CanceledException
+ * when signing was canceled (eg., user aborted when entering
+ * passphrase)
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws UnsupportedSigningFormatException
+ * if a config is given and the wanted key format is not
+ * supported
+ */
+ GpgSignature sign(@NonNull Repository repository, @NonNull GpgConfig config,
+ byte[] data, @NonNull PersonIdent committer, String signingKey,
+ CredentialsProvider credentialsProvider) throws CanceledException,
+ IOException, UnsupportedSigningFormatException;
+
+ /**
+ * Indicates if a signing key is available for the specified committer
+ * and/or signing key.
+ *
+ * @param repository
+ * the current {@link Repository}
+ * @param config
+ * GPG settings from the git config
+ * @param committer
+ * the signing identity (to help with key lookup in case signing
+ * key is not specified)
+ * @param signingKey
+ * if non-{@code null} overrides the signing key from the config
+ * @param credentialsProvider
+ * provider to use when querying for signing key credentials (eg.
+ * passphrase)
+ * @return {@code true} if a signing key is available, {@code false}
+ * otherwise
+ * @throws CanceledException
+ * when signing was canceled (eg., user aborted when entering
+ * passphrase)
+ */
+ boolean canLocateSigningKey(@NonNull Repository repository,
+ @NonNull GpgConfig config, @NonNull PersonIdent committer,
+ String signingKey, CredentialsProvider credentialsProvider)
+ throws CanceledException;
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignerFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignerFactory.java
new file mode 100644
index 0000000000..125d25e3b7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignerFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * A factory for {@link Signer}s.
+ *
+ * @since 7.0
+ */
+public interface SignerFactory {
+
+ /**
+ * Tells what kind of {@link Signer} this factory creates.
+ *
+ * @return the {@link GpgConfig.GpgFormat} of the signer
+ */
+ @NonNull
+ GpgConfig.GpgFormat getType();
+
+ /**
+ * Creates a new instance of a {@link Signer} that can produce signatures of
+ * type {@link #getType()}.
+ *
+ * @return a new {@link Signer}
+ */
+ @NonNull
+ Signer create();
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Signers.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Signers.java
new file mode 100644
index 0000000000..7771b07841
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Signers.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.text.MessageFormat;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.JGitText;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Manages the available signers.
+ *
+ * @since 7.0
+ */
+public final class Signers {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Signers.class);
+
+ private static final Map<GpgConfig.GpgFormat, SignerFactory> SIGNER_FACTORIES = loadSigners();
+
+ private static final Map<GpgConfig.GpgFormat, Signer> SIGNERS = new ConcurrentHashMap<>();
+
+ private static Map<GpgConfig.GpgFormat, SignerFactory> loadSigners() {
+ Map<GpgConfig.GpgFormat, SignerFactory> result = new EnumMap<>(
+ GpgConfig.GpgFormat.class);
+ try {
+ for (SignerFactory factory : ServiceLoader
+ .load(SignerFactory.class)) {
+ GpgConfig.GpgFormat format = factory.getType();
+ SignerFactory existing = result.get(format);
+ if (existing != null) {
+ LOG.warn("{}", //$NON-NLS-1$
+ MessageFormat.format(
+ JGitText.get().signatureServiceConflict,
+ "SignerFactory", format, //$NON-NLS-1$
+ existing.getClass().getCanonicalName(),
+ factory.getClass().getCanonicalName()));
+ } else {
+ result.put(format, factory);
+ }
+ }
+ } catch (ServiceConfigurationError e) {
+ LOG.error(e.getMessage(), e);
+ }
+ return result;
+ }
+
+ private Signers() {
+ // No instantiation
+ }
+
+ /**
+ * Retrieves a {@link Signer} that can produce signatures of the given type
+ * {@code format}.
+ *
+ * @param format
+ * {@link GpgConfig.GpgFormat} the signer must support
+ * @return a {@link Signer}, or {@code null} if none is available
+ */
+ public static Signer get(@NonNull GpgConfig.GpgFormat format) {
+ return SIGNERS.computeIfAbsent(format, f -> {
+ SignerFactory factory = SIGNER_FACTORIES.get(format);
+ if (factory == null) {
+ return null;
+ }
+ return factory.create();
+ });
+ }
+
+ /**
+ * Sets a specific signer to use for a specific signature type.
+ *
+ * @param format
+ * signature type to set the {@code signer} for
+ * @param signer
+ * the {@link Signer} to use for signatures of type
+ * {@code format}; if {@code null}, a default implementation, if
+ * available, may be used.
+ */
+ public static void set(@NonNull GpgConfig.GpgFormat format, Signer signer) {
+ if (signer == null) {
+ SIGNERS.remove(format);
+ } else {
+ SIGNERS.put(format, signer);
+ }
+ }
+}
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 bbc614448f..ea73d95102 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
@@ -206,23 +206,6 @@ public class TagBuilder extends ObjectBuilder {
return os.toByteArray();
}
- /**
- * 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, or {@code null} if the tag cannot be
- * encoded
- * @deprecated since 5.11; use {@link #build()} instead
- */
- @Deprecated
- public byte[] toByteArray() {
- try {
- return build();
- } catch (UnsupportedEncodingException e) {
- return null;
- }
- }
-
@SuppressWarnings("nls")
@Override
public String toString() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java
index 0c03adcab8..3d4e0d1f3c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java
@@ -17,6 +17,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.util.FS;
@@ -50,11 +51,36 @@ public interface TypedConfigGetter {
* default value to return if no value was present.
* @return true if any value or defaultValue is true, false for missing or
* explicit false
+ * @deprecated use
+ * {@link #getBoolean(Config, String, String, String, Boolean)}
+ * instead
*/
+ @Deprecated
boolean getBoolean(Config config, String section, String subsection,
String name, boolean defaultValue);
/**
+ * Get a boolean value from a git {@link Config}.
+ *
+ * @param config
+ * to get the value from
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @param defaultValue
+ * default value to return if no value was present.
+ * @return true if any value or defaultValue is true, false for missing or
+ * explicit false
+ * @since 7.2
+ */
+ @Nullable
+ Boolean getBoolean(Config config, String section, String subsection,
+ String name, @Nullable Boolean defaultValue);
+
+ /**
* Parse an enumeration from a git {@link Config}.
*
* @param <T>
@@ -74,8 +100,9 @@ public interface TypedConfigGetter {
* default value to return if no value was present.
* @return the selected enumeration value, or {@code defaultValue}.
*/
+ @Nullable
<T extends Enum<?>> T getEnum(Config config, T[] all, String section,
- String subsection, String name, T defaultValue);
+ String subsection, String name, @Nullable T defaultValue);
/**
* Obtain an integer value from a git {@link Config}.
@@ -91,11 +118,34 @@ public interface TypedConfigGetter {
* @param defaultValue
* default value to return if no value was present.
* @return an integer value from the configuration, or defaultValue.
+ * @deprecated use {@link #getInt(Config, String, String, String, Integer)}
+ * instead
*/
+ @Deprecated
int getInt(Config config, String section, String subsection, String name,
int defaultValue);
/**
+ * Obtain an integer value from a git {@link Config}.
+ *
+ * @param config
+ * to get the value from
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @param defaultValue
+ * default value to return if no value was present.
+ * @return an integer value from the configuration, or defaultValue.
+ * @since 7.2
+ */
+ @Nullable
+ Integer getInt(Config config, String section, String subsection,
+ String name, @Nullable Integer defaultValue);
+
+ /**
* Obtain an integer value from a git {@link Config} which must be in given
* range.
*
@@ -117,11 +167,43 @@ public interface TypedConfigGetter {
* @return an integer value from the configuration, or defaultValue.
* {@code #UNSET_INT} if unset.
* @since 6.1
+ * @deprecated use
+ * {@link #getIntInRange(Config, String, String, String, int, int, Integer)}
+ * instead
*/
+ @Deprecated
int getIntInRange(Config config, String section, String subsection,
String name, int minValue, int maxValue, int defaultValue);
/**
+ * Obtain an integer value from a git {@link Config} which must be in given
+ * range.
+ *
+ * @param config
+ * to get the value from
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @param minValue
+ * minimal value
+ * @param maxValue
+ * maximum value
+ * @param defaultValue
+ * default value to return if no value was present. Use
+ * {@code #UNSET_INT} to set the default to unset.
+ * @return an integer value from the configuration, or defaultValue.
+ * {@code #UNSET_INT} if unset.
+ * @since 7.2
+ */
+ @Nullable
+ Integer getIntInRange(Config config, String section, String subsection,
+ String name, int minValue, int maxValue,
+ @Nullable Integer defaultValue);
+
+ /**
* Obtain a long value from a git {@link Config}.
*
* @param config
@@ -135,11 +217,34 @@ public interface TypedConfigGetter {
* @param defaultValue
* default value to return if no value was present.
* @return a long value from the configuration, or defaultValue.
+ * @deprecated use {@link #getLong(Config, String, String, String, Long)}
+ * instead
*/
+ @Deprecated
long getLong(Config config, String section, String subsection, String name,
long defaultValue);
/**
+ * Obtain a long value from a git {@link Config}.
+ *
+ * @param config
+ * to get the value from
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @param defaultValue
+ * default value to return if no value was present.
+ * @return a long value from the configuration, or defaultValue.
+ * @since 7.2
+ */
+ @Nullable
+ Long getLong(Config config, String section, String subsection, String name,
+ @Nullable Long defaultValue);
+
+ /**
* Parse a numerical time unit, such as "1 minute", from a git
* {@link Config}.
*
@@ -159,11 +264,41 @@ public interface TypedConfigGetter {
* indication of the units.
* @return the value, or {@code defaultValue} if not set, expressed in
* {@code units}.
+ * @deprecated use
+ * {@link #getTimeUnit(Config, String, String, String, Long, TimeUnit)}
+ * instead
*/
+ @Deprecated
long getTimeUnit(Config config, String section, String subsection,
String name, long defaultValue, TimeUnit wantUnit);
/**
+ * Parse a numerical time unit, such as "1 minute", from a git
+ * {@link Config}.
+ *
+ * @param config
+ * to get the value from
+ * @param section
+ * section the key is in.
+ * @param subsection
+ * subsection the key is in, or null if not in a subsection.
+ * @param name
+ * the key name.
+ * @param defaultValue
+ * default value to return if no value was present.
+ * @param wantUnit
+ * the units of {@code defaultValue} and the return value, as
+ * well as the units to assume if the value does not contain an
+ * indication of the units.
+ * @return the value, or {@code defaultValue} if not set, expressed in
+ * {@code units}.
+ * @since 7.2
+ */
+ @Nullable
+ Long getTimeUnit(Config config, String section, String subsection,
+ String name, @Nullable Long defaultValue, TimeUnit wantUnit);
+
+ /**
* Parse a string value from a git {@link Config} and treat it as a file
* path, replacing a ~/ prefix by the user's home directory.
* <p>
@@ -189,9 +324,10 @@ public interface TypedConfigGetter {
* @return the {@link Path}, or {@code defaultValue} if not set
* @since 5.10
*/
+ @Nullable
default Path getPath(Config config, String section, String subsection,
String name, @NonNull FS fs, File resolveAgainst,
- Path defaultValue) {
+ @Nullable Path defaultValue) {
String value = config.getString(section, subsection, name);
if (value == null) {
return defaultValue;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ContentMergeStrategy.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ContentMergeStrategy.java
index 6d568643d5..a835a1dfc5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ContentMergeStrategy.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ContentMergeStrategy.java
@@ -23,5 +23,12 @@ public enum ContentMergeStrategy {
OURS,
/** Resolve the conflict hunk using the theirs version. */
- THEIRS
-} \ No newline at end of file
+ THEIRS,
+
+ /**
+ * Resolve the conflict hunk using a union of both ours and theirs versions.
+ *
+ * @since 6.10.1
+ */
+ UNION
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
index 5734a25276..d0d4d367b9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
@@ -120,6 +120,7 @@ public final class MergeAlgorithm {
result.add(1, 0, 0, ConflictState.NO_CONFLICT);
break;
case THEIRS:
+ case UNION:
result.add(2, 0, theirs.size(),
ConflictState.NO_CONFLICT);
break;
@@ -148,6 +149,7 @@ public final class MergeAlgorithm {
// we modified, they deleted
switch (strategy) {
case OURS:
+ case UNION:
result.add(1, 0, ours.size(), ConflictState.NO_CONFLICT);
break;
case THEIRS:
@@ -158,7 +160,7 @@ public final class MergeAlgorithm {
result.add(1, 0, ours.size(),
ConflictState.FIRST_CONFLICTING_RANGE);
result.add(0, 0, base.size(),
- ConflictState.BASE_CONFLICTING_RANGE);
+ ConflictState.BASE_CONFLICTING_RANGE);
result.add(2, 0, 0, ConflictState.NEXT_CONFLICTING_RANGE);
break;
}
@@ -333,6 +335,15 @@ public final class MergeAlgorithm {
theirsEndB - commonSuffix,
ConflictState.NO_CONFLICT);
break;
+ case UNION:
+ result.add(1, oursBeginB + commonPrefix,
+ oursEndB - commonSuffix,
+ ConflictState.NO_CONFLICT);
+
+ result.add(2, theirsBeginB + commonPrefix,
+ theirsEndB - commonSuffix,
+ ConflictState.NO_CONFLICT);
+ break;
default:
result.add(1, oursBeginB + commonPrefix,
oursEndB - commonSuffix,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
index a35b30eb01..079db4a07f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
@@ -22,37 +22,6 @@ import org.eclipse.jgit.diff.RawText;
* A class to convert merge results into a Git conformant textual presentation
*/
public class MergeFormatter {
- /**
- * Formats the results of a merge of {@link org.eclipse.jgit.diff.RawText}
- * objects in a Git conformant way. This method also assumes that the
- * {@link org.eclipse.jgit.diff.RawText} objects being merged are line
- * oriented files which use LF as delimiter. This method will also use LF to
- * separate chunks and conflict metadata, therefore it fits only to texts
- * that are LF-separated lines.
- *
- * @param out
- * the output stream where to write the textual presentation
- * @param res
- * the merge result which should be presented
- * @param seqName
- * When a conflict is reported each conflicting range will get a
- * name. This name is following the "&lt;&lt;&lt;&lt;&lt;&lt;&lt;
- * " or "&gt;&gt;&gt;&gt;&gt;&gt;&gt; " conflict markers. The
- * names for the sequences are given in this list
- * @param charsetName
- * the name of the character set used when writing conflict
- * metadata
- * @throws java.io.IOException
- * if an IO error occurred
- * @deprecated Use
- * {@link #formatMerge(OutputStream, MergeResult, List, Charset)}
- * instead.
- */
- @Deprecated
- public void formatMerge(OutputStream out, MergeResult<RawText> res,
- List<String> seqName, String charsetName) throws IOException {
- formatMerge(out, res, seqName, Charset.forName(charsetName));
- }
/**
* Formats the results of a merge of {@link org.eclipse.jgit.diff.RawText}
@@ -129,40 +98,6 @@ public class MergeFormatter {
* the name ranges from ours should get
* @param theirsName
* the name ranges from theirs should get
- * @param charsetName
- * the name of the character set used when writing conflict
- * metadata
- * @throws java.io.IOException
- * if an IO error occurred
- * @deprecated use
- * {@link #formatMerge(OutputStream, MergeResult, String, String, String, Charset)}
- * instead.
- */
- @Deprecated
- public void formatMerge(OutputStream out, MergeResult res, String baseName,
- String oursName, String theirsName, String charsetName) throws IOException {
- formatMerge(out, res, baseName, oursName, theirsName,
- Charset.forName(charsetName));
- }
-
- /**
- * Formats the results of a merge of exactly two
- * {@link org.eclipse.jgit.diff.RawText} objects in a Git conformant way.
- * This convenience method accepts the names for the three sequences (base
- * and the two merged sequences) as explicit parameters and doesn't require
- * the caller to specify a List
- *
- * @param out
- * the {@link java.io.OutputStream} where to write the textual
- * presentation
- * @param res
- * the merge result which should be presented
- * @param baseName
- * the name ranges from the base should get
- * @param oursName
- * the name ranges from ours should get
- * @param theirsName
- * the name ranges from theirs should get
* @param charset
* the character set used when writing conflict metadata
* @throws java.io.IOException
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
index e0c083f55c..039d7d844c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
@@ -92,24 +92,6 @@ public class MergeMessageFormatter {
}
/**
- * Add section with conflicting paths to merge message. Lines are prefixed
- * with a hash.
- *
- * @param message
- * the original merge message
- * @param conflictingPaths
- * the paths with conflicts
- * @return merge message with conflicting paths added
- * @deprecated since 6.1; use
- * {@link #formatWithConflicts(String, Iterable, char)} instead
- */
- @Deprecated
- public String formatWithConflicts(String message,
- List<String> conflictingPaths) {
- return formatWithConflicts(message, conflictingPaths, '#');
- }
-
- /**
* Add section with conflicting paths to merge message.
*
* @param message
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
index 1162a615f2..f58ef4faba 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
@@ -18,10 +18,11 @@ package org.eclipse.jgit.merge;
import java.io.IOException;
import java.text.MessageFormat;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
-import java.util.TimeZone;
+import java.util.stream.Collectors;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -185,12 +186,15 @@ public class RecursiveMerger extends ResolveMerger {
if (mergeTrees(bcTree, currentBase.getTree(),
nextBase.getTree(), true))
currentBase = createCommitForTree(resultTree, parents);
- else
+ else {
+ String failedPaths = failingPathsMessage();
throw new NoMergeBaseException(
NoMergeBaseException.MergeBaseFailureReason.CONFLICTS_DURING_MERGE_BASE_CALCULATION,
MessageFormat.format(
JGitText.get().mergeRecursiveConflictsWhenMergingCommonAncestors,
- currentBase.getName(), nextBase.getName()));
+ currentBase.getName(), nextBase.getName(),
+ failedPaths));
+ }
}
} finally {
inCore = oldIncore;
@@ -229,11 +233,23 @@ public class RecursiveMerger extends ResolveMerger {
private static PersonIdent mockAuthor(List<RevCommit> parents) {
String name = RecursiveMerger.class.getSimpleName();
int time = 0;
- for (RevCommit p : parents)
+ for (RevCommit p : parents) {
time = Math.max(time, p.getCommitTime());
- return new PersonIdent(
- name, name + "@JGit", //$NON-NLS-1$
- new Date((time + 1) * 1000L),
- TimeZone.getTimeZone("GMT+0000")); //$NON-NLS-1$
+ }
+ return new PersonIdent(name, name + "@JGit", //$NON-NLS-1$
+ Instant.ofEpochSecond(time+1), ZoneOffset.UTC);
+ }
+
+ private String failingPathsMessage() {
+ int max = 25;
+ String failedPaths = failingPaths.entrySet().stream().limit(max)
+ .map(entry -> entry.getKey() + ":" + entry.getValue()) //$NON-NLS-1$
+ .collect(Collectors.joining("\n")); //$NON-NLS-1$
+
+ if (failingPaths.size() > max) {
+ failedPaths = String.format("%s\n... (%s failing paths omitted)", //$NON-NLS-1$
+ failedPaths, Integer.valueOf(failingPaths.size() - max));
+ }
+ return failedPaths;
}
}
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 1ad41be423..dc96f65b87 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -41,6 +41,7 @@ import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.attributes.Attribute;
import org.eclipse.jgit.attributes.Attributes;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
import org.eclipse.jgit.diff.DiffAlgorithm;
import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
import org.eclipse.jgit.diff.RawText;
@@ -837,6 +838,13 @@ public class ResolveMerger extends ThreeWayMerger {
@NonNull
private ContentMergeStrategy contentStrategy = ContentMergeStrategy.CONFLICT;
+ /**
+ * The {@link AttributesNodeProvider} to use while merging trees.
+ *
+ * @since 6.10.1
+ */
+ protected AttributesNodeProvider attributesNodeProvider;
+
private static MergeAlgorithm getMergeAlgorithm(Config config) {
SupportedAlgorithm diffAlg = config.getEnum(
CONFIG_DIFF_SECTION, null, CONFIG_KEY_ALGORITHM,
@@ -1273,6 +1281,13 @@ public class ResolveMerger extends ThreeWayMerger {
default:
break;
}
+ if (ignoreConflicts) {
+ // If the path is selected to be treated as binary via attributes, we do not perform
+ // content merge. When ignoreConflicts = true, we simply keep OURS to allow virtual commit
+ // to be built.
+ keep(ourDce);
+ return true;
+ }
// add the conflicting path to merge result
String currentPath = tw.getPathString();
MergeResult<RawText> result = new MergeResult<>(
@@ -1312,8 +1327,12 @@ public class ResolveMerger extends ThreeWayMerger {
addToCheckout(currentPath, null, attributes);
return true;
} catch (BinaryBlobException e) {
- // if the file is binary in either OURS, THEIRS or BASE
- // here, we don't have an option to ignore conflicts
+ // The file is binary in either OURS, THEIRS or BASE
+ if (ignoreConflicts) {
+ // When ignoreConflicts = true, we simply keep OURS to allow virtual commit to be built.
+ keep(ourDce);
+ return true;
+ }
}
}
switch (getContentMergeStrategy()) {
@@ -1354,6 +1373,8 @@ public class ResolveMerger extends ThreeWayMerger {
}
}
} else {
+ // This is reachable if contentMerge() call above threw BinaryBlobException, so we don't
+ // need to check ignoreConflicts here, since it's already handled above.
result.setContainsConflicts(true);
addConflict(base, ours, theirs);
unmergedPaths.add(currentPath);
@@ -1489,11 +1510,26 @@ public class ResolveMerger extends ThreeWayMerger {
: getRawText(ours.getEntryObjectId(), attributes[T_OURS]);
RawText theirsText = theirs == null ? RawText.EMPTY_TEXT
: getRawText(theirs.getEntryObjectId(), attributes[T_THEIRS]);
- mergeAlgorithm.setContentMergeStrategy(strategy);
+ mergeAlgorithm.setContentMergeStrategy(
+ getAttributesContentMergeStrategy(attributes[T_OURS],
+ strategy));
return mergeAlgorithm.merge(RawTextComparator.DEFAULT, baseText,
ourText, theirsText);
}
+ private ContentMergeStrategy getAttributesContentMergeStrategy(
+ Attributes attributes, ContentMergeStrategy strategy) {
+ Attribute attr = attributes.get(Constants.ATTR_MERGE);
+ if (attr != null) {
+ String attrValue = attr.getValue();
+ if (attrValue != null && attrValue
+ .equals(Constants.ATTR_BUILTIN_UNION_MERGE_DRIVER)) {
+ return ContentMergeStrategy.UNION;
+ }
+ }
+ return strategy;
+ }
+
private boolean isIndexDirty() {
if (inCore) {
return false;
@@ -1824,6 +1860,18 @@ public class ResolveMerger extends ThreeWayMerger {
this.workingTreeIterator = workingTreeIterator;
}
+ /**
+ * Sets the {@link AttributesNodeProvider} to be used by this merger.
+ *
+ * @param attributesNodeProvider
+ * the attributeNodeProvider to set
+ * @since 6.10.1
+ */
+ public void setAttributesNodeProvider(
+ AttributesNodeProvider attributesNodeProvider) {
+ this.attributesNodeProvider = attributesNodeProvider;
+ }
+
/**
* The resolve conflict way of three way merging
@@ -1868,6 +1916,9 @@ public class ResolveMerger extends ThreeWayMerger {
WorkTreeUpdater.createWorkTreeUpdater(db, dircache);
dircache = workTreeUpdater.getLockedDirCache();
tw = new NameConflictTreeWalk(db, reader);
+ if (attributesNodeProvider != null) {
+ tw.setAttributesNodeProvider(attributesNodeProvider);
+ }
tw.addTree(baseTree);
tw.setHead(tw.addTree(headTree));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java
index 79ceb1316a..30512c17b8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java
@@ -199,7 +199,7 @@ public class NoteMapMerger {
if (child == null)
return;
if (child instanceof InMemoryNoteBucket)
- b.setBucket(cell, ((InMemoryNoteBucket) child).writeTree(inserter));
+ b.setBucket(cell, child.writeTree(inserter));
else
b.setBucket(cell, child.getTreeId());
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java
index a327095c81..23e09b9479 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java
@@ -23,6 +23,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
@@ -33,12 +34,13 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.InflaterInputStream;
+
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.FilterFailedException;
-import org.eclipse.jgit.api.errors.PatchFormatException;
import org.eclipse.jgit.attributes.Attribute;
import org.eclipse.jgit.attributes.Attributes;
import org.eclipse.jgit.attributes.FilterCommand;
@@ -101,11 +103,12 @@ import org.eclipse.jgit.util.sha1.SHA1;
* @since 6.4
*/
public class PatchApplier {
-
private static final byte[] NO_EOL = "\\ No newline at end of file" //$NON-NLS-1$
.getBytes(StandardCharsets.US_ASCII);
- /** The tree before applying the patch. Only non-null for inCore operation. */
+ /**
+ * The tree before applying the patch. Only non-null for inCore operation.
+ */
@Nullable
private final RevTree beforeTree;
@@ -115,10 +118,14 @@ public class PatchApplier {
private final ObjectReader reader;
+ private final Charset charset;
+
private WorkingTreeOptions workingTreeOptions;
private int inCoreSizeLimit;
+ private boolean allowConflicts;
+
/**
* @param repo
* repository to apply the patch in
@@ -128,7 +135,8 @@ public class PatchApplier {
inserter = repo.newObjectInserter();
reader = inserter.newReader();
beforeTree = null;
-
+ allowConflicts = false;
+ charset = StandardCharsets.UTF_8;
Config config = repo.getConfig();
workingTreeOptions = config.get(WorkingTreeOptions.KEY);
inCoreSizeLimit = config.getInt(ConfigConstants.CONFIG_MERGE_SECTION,
@@ -143,11 +151,14 @@ public class PatchApplier {
* @param oi
* to be used for modifying objects
*/
- public PatchApplier(Repository repo, RevTree beforeTree, ObjectInserter oi) {
+ public PatchApplier(Repository repo, RevTree beforeTree,
+ ObjectInserter oi) {
this.repo = repo;
this.beforeTree = beforeTree;
inserter = oi;
reader = oi.newReader();
+ allowConflicts = false;
+ charset = StandardCharsets.UTF_8;
}
/**
@@ -157,7 +168,6 @@ public class PatchApplier {
* @since 6.3
*/
public static class Result {
-
/**
* A wrapper for a patch applying error that affects a given file.
*
@@ -166,28 +176,68 @@ public class PatchApplier {
// TODO(ms): rename this class in next major release
@SuppressWarnings("JavaLangClash")
public static class Error {
+ final String msg;
+
+ final String oldFileName;
- private String msg;
- private String oldFileName;
- private @Nullable HunkHeader hh;
+ @Nullable
+ final HunkHeader hh;
- private Error(String msg, String oldFileName,
- @Nullable HunkHeader hh) {
+ final boolean isGitConflict;
+
+ Error(String msg, String oldFileName, @Nullable HunkHeader hh,
+ boolean isGitConflict) {
this.msg = msg;
this.oldFileName = oldFileName;
this.hh = hh;
+ this.isGitConflict = isGitConflict;
+ }
+
+ /**
+ * Signals if as part of encountering this error, conflict markers
+ * were added to the file.
+ *
+ * @return {@code true} if conflict markers were added for this
+ * error.
+ *
+ * @since 6.10
+ */
+ public boolean isGitConflict() {
+ return isGitConflict;
}
@Override
public String toString() {
if (hh != null) {
- return MessageFormat.format(JGitText.get().patchApplyErrorWithHunk,
- oldFileName, hh, msg);
+ return MessageFormat.format(
+ JGitText.get().patchApplyErrorWithHunk, oldFileName,
+ hh, msg);
}
- return MessageFormat.format(JGitText.get().patchApplyErrorWithoutHunk,
- oldFileName, msg);
+ return MessageFormat.format(
+ JGitText.get().patchApplyErrorWithoutHunk, oldFileName,
+ msg);
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || !(o instanceof Error)) {
+ return false;
+ }
+ Error error = (Error) o;
+ return Objects.equals(msg, error.msg)
+ && Objects.equals(oldFileName, error.oldFileName)
+ && Objects.equals(hh, error.hh)
+ && isGitConflict == error.isGitConflict;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(msg, oldFileName, hh,
+ Boolean.valueOf(isGitConflict));
+ }
}
private ObjectId treeId;
@@ -225,35 +275,15 @@ public class PatchApplier {
return errors;
}
- private void addError(String msg,String oldFileName, @Nullable HunkHeader hh) {
- errors.add(new Error(msg, oldFileName, hh));
+ private void addError(String msg, String oldFileName,
+ @Nullable HunkHeader hh) {
+ errors.add(new Error(msg, oldFileName, hh, false));
}
- }
- /**
- * Applies the given patch
- *
- * @param patchInput
- * the patch to apply.
- * @return the result of the patch
- * @throws PatchFormatException
- * if the patch cannot be parsed
- * @throws IOException
- * if the patch read fails
- * @deprecated use {@link #applyPatch(Patch)} instead
- */
- @Deprecated
- public Result applyPatch(InputStream patchInput)
- throws PatchFormatException, IOException {
- Patch p = new Patch();
- try (InputStream inStream = patchInput) {
- p.parse(inStream);
-
- if (!p.getErrors().isEmpty()) {
- throw new PatchFormatException(p.getErrors());
- }
+ private void addErrorWithGitConflict(String msg, String oldFileName,
+ @Nullable HunkHeader hh) {
+ errors.add(new Error(msg, oldFileName, hh, true));
}
- return applyPatch(p);
}
/**
@@ -357,6 +387,17 @@ public class PatchApplier {
return result;
}
+ /**
+ * Sets up the {@link PatchApplier} to apply patches even if they conflict.
+ *
+ * @return the {@link PatchApplier} to apply any patches
+ * @since 6.10
+ */
+ public PatchApplier allowConflicts() {
+ allowConflicts = true;
+ return this;
+ }
+
private File getFile(String path) {
return inCore() ? null : new File(repo.getWorkTree(), path);
}
@@ -439,6 +480,7 @@ public class PatchApplier {
return false;
}
}
+
private static final int FILE_TREE_INDEX = 1;
/**
@@ -539,7 +581,9 @@ public class PatchApplier {
convertCrLf);
resultStreamLoader = applyText(raw, fh, result);
}
- if (resultStreamLoader == null || !result.getErrors().isEmpty()) {
+ if (resultStreamLoader == null
+ || (!result.getErrors().isEmpty() && result.getErrors().stream()
+ .anyMatch(e -> !e.msg.equals("cannot apply hunk")))) { //$NON-NLS-1$
return;
}
@@ -961,9 +1005,51 @@ public class PatchApplier {
}
}
if (!applies) {
- result.addError(JGitText.get().applyTextPatchCannotApplyHunk,
- fh.getOldPath(), hh);
- return null;
+ if (!allowConflicts) {
+ result.addError(
+ JGitText.get().applyTextPatchCannotApplyHunk,
+ fh.getOldPath(), hh);
+ return null;
+ }
+ // Insert conflict markers. This is best-guess because the
+ // file might have changed completely. But at least we give
+ // the user a graceful state that they can resolve manually.
+ // An alternative to this is using the 3-way merger. This
+ // only works if the pre-image SHA is contained in the repo.
+ // If that was the case, cherry-picking the original commit
+ // should be preferred to apply a patch.
+ result.addErrorWithGitConflict("cannot apply hunk", fh.getOldPath(), hh); //$NON-NLS-1$
+ newLines.add(Math.min(applyAt++, newLines.size()),
+ asBytes("<<<<<<< HEAD")); //$NON-NLS-1$
+ applyAt += hh.getOldImage().lineCount;
+ newLines.add(Math.min(applyAt++, newLines.size()),
+ asBytes("=======")); //$NON-NLS-1$
+
+ int sz = hunkLines.size();
+ for (int j = 1; j < sz; j++) {
+ ByteBuffer hunkLine = hunkLines.get(j);
+ if (!hunkLine.hasRemaining()) {
+ // Completely empty line; accept as empty context
+ // line
+ applyAt++;
+ lastWasRemoval = false;
+ continue;
+ }
+ switch (hunkLine.array()[hunkLine.position()]) {
+ case ' ':
+ case '+':
+ newLines.add(Math.min(applyAt++, newLines.size()),
+ slice(hunkLine, 1));
+ break;
+ case '-':
+ case '\\':
+ default:
+ break;
+ }
+ }
+ newLines.add(Math.min(applyAt++, newLines.size()),
+ asBytes(">>>>>>> PATCH")); //$NON-NLS-1$
+ continue;
}
// Hunk applies at applyAt. Apply it, and update afterLastHunk and
// lineNumberShift
@@ -1010,7 +1096,11 @@ public class PatchApplier {
} else if (!rt.isMissingNewlineAtEnd()) {
newLines.add(null);
}
+ return toContentStreamLoader(newLines);
+ }
+ private static ContentStreamLoader toContentStreamLoader(
+ List<ByteBuffer> newLines) throws IOException {
// We could check if old == new, but the short-circuiting complicates
// logic for inCore patching, so just write the new thing regardless.
TemporaryBuffer buffer = new TemporaryBuffer.LocalFile(null);
@@ -1034,6 +1124,10 @@ public class PatchApplier {
}
}
+ private ByteBuffer asBytes(String str) {
+ return ByteBuffer.wrap(str.getBytes(charset));
+ }
+
@SuppressWarnings("ByteBufferBackingArray")
private boolean canApplyAt(List<ByteBuffer> hunkLines,
List<ByteBuffer> newLines, int line) {
@@ -1123,4 +1217,4 @@ public class PatchApplier {
in.close();
}
}
-}
+} \ No newline at end of file
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 82671d9abb..7c763bc9b8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
@@ -43,7 +43,7 @@ import org.eclipse.jgit.util.RawParseUtils;
* Tree and blob objects reachable from interesting commits are automatically
* scheduled for inclusion in the results of {@link #nextObject()}, returning
* each object exactly once. Objects are sorted and returned according to the
- * the commits that reference them and the order they appear within a tree.
+ * commits that reference them and the order they appear within a tree.
* Ordering can be affected by changing the
* {@link org.eclipse.jgit.revwalk.RevSort} used to order the commits that are
* returned first.
@@ -164,29 +164,6 @@ public class ObjectWalk extends RevWalk {
}
/**
- * Create an object reachability checker that will use bitmaps if possible.
- *
- * This reachability checker accepts any object as target. For checks
- * exclusively between commits, see
- * {@link RevWalk#createReachabilityChecker()}.
- *
- * @return an object reachability checker, using bitmaps if possible.
- *
- * @throws IOException
- * when the index fails to load.
- *
- * @since 5.8
- * @deprecated use
- * {@code ObjectReader#createObjectReachabilityChecker(ObjectWalk)}
- * instead.
- */
- @Deprecated
- public final ObjectReachabilityChecker createObjectReachabilityChecker()
- throws IOException {
- return reader.createObjectReachabilityChecker(this);
- }
-
- /**
* Mark an object or commit to start graph traversal from.
* <p>
* Callers are encouraged to use
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java
index 1a869a0703..5afb669a15 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java
@@ -26,40 +26,6 @@ import org.eclipse.jgit.errors.MissingObjectException;
* @since 5.4
*/
public interface ReachabilityChecker {
-
- /**
- * Check if all targets are reachable from the {@code starters} commits.
- * <p>
- * Caller should parse the objectIds (preferably with
- * {@code walk.parseCommit()} and handle missing/incorrect type objects
- * before calling this method.
- *
- * @param targets
- * commits to reach.
- * @param starters
- * known starting points.
- * @return An unreachable target if at least one of the targets is
- * unreachable. An empty optional if all targets are reachable from
- * the starters.
- *
- * @throws MissingObjectException
- * if any of the incoming objects doesn't exist in the
- * repository.
- * @throws IncorrectObjectTypeException
- * if any of the incoming objects is not a commit or a tag.
- * @throws IOException
- * if any of the underlying indexes or readers can not be
- * opened.
- *
- * @deprecated see {{@link #areAllReachable(Collection, Stream)}
- */
- @Deprecated
- default Optional<RevCommit> areAllReachable(Collection<RevCommit> targets,
- Collection<RevCommit> starters) throws MissingObjectException,
- IncorrectObjectTypeException, IOException {
- return areAllReachable(targets, starters.stream());
- }
-
/**
* Check if all targets are reachable from the {@code starters} commits.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
index 743a8ccce0..871545fca2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2009 Google Inc.
+ * Copyright (C) 2008, 2024 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
@@ -401,13 +401,13 @@ public class RevCommit extends RevObject {
* @since 5.1
*/
public final byte[] getRawGpgSignature() {
- final byte[] raw = buffer;
- final byte[] header = { 'g', 'p', 'g', 's', 'i', 'g' };
- final int start = RawParseUtils.headerStart(header, raw, 0);
+ byte[] raw = buffer;
+ byte[] header = { 'g', 'p', 'g', 's', 'i', 'g' };
+ int start = RawParseUtils.headerStart(header, raw, 0);
if (start < 0) {
return null;
}
- final int end = RawParseUtils.headerEnd(raw, start);
+ int end = RawParseUtils.nextLfSkippingSplitLines(raw, start);
return RawParseUtils.headerValue(raw, start, end);
}
@@ -524,6 +524,30 @@ public class RevCommit extends RevObject {
}
/**
+ * Parse the commit message and return its first line, i.e., everything up
+ * to but not including the first newline, if any.
+ *
+ * @return the first line of the decoded commit message as a string; never
+ * {@code null}.
+ * @since 7.2
+ */
+ public final String getFirstMessageLine() {
+ int msgB = RawParseUtils.commitMessage(buffer, 0);
+ if (msgB < 0) {
+ return ""; //$NON-NLS-1$
+ }
+ int msgE = msgB;
+ byte[] raw = buffer;
+ while (msgE < raw.length && raw[msgE] != '\n') {
+ msgE++;
+ }
+ if (msgE > msgB && msgE > 0 && raw[msgE - 1] == '\r') {
+ msgE--;
+ }
+ return RawParseUtils.decode(guessEncoding(buffer), buffer, msgB, msgE);
+ }
+
+ /**
* Determine the encoding of the commit message buffer.
* <p>
* Locates the "encoding" header (if present) and returns its value. Due to
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 75dbd57740..0737a78085 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
@@ -38,8 +38,17 @@ import org.eclipse.jgit.util.StringUtils;
*/
public class RevTag extends RevObject {
- private static final byte[] hSignature = Constants
- .encodeASCII("-----BEGIN PGP SIGNATURE-----"); //$NON-NLS-1$
+ private static final byte[] SIGNATURE_START = Constants
+ .encodeASCII("-----BEGIN"); //$NON-NLS-1$
+
+ private static final byte[] GPG_SIGNATURE_START = Constants
+ .encodeASCII(Constants.GPG_SIGNATURE_PREFIX);
+
+ private static final byte[] CMS_SIGNATURE_START = Constants
+ .encodeASCII(Constants.CMS_SIGNATURE_PREFIX);
+
+ private static final byte[] SSH_SIGNATURE_START = Constants
+ .encodeASCII(Constants.SSH_SIGNATURE_PREFIX);
/**
* Parse an annotated tag from its canonical format.
@@ -208,20 +217,27 @@ public class RevTag extends RevObject {
return msgB;
}
// Find the last signature start and return the rest
- int start = nextStart(hSignature, raw, msgB);
+ int start = nextStart(SIGNATURE_START, raw, msgB);
if (start < 0) {
return start;
}
int next = RawParseUtils.nextLF(raw, start);
while (next < raw.length) {
- int newStart = nextStart(hSignature, raw, next);
+ int newStart = nextStart(SIGNATURE_START, raw, next);
if (newStart < 0) {
break;
}
start = newStart;
next = RawParseUtils.nextLF(raw, start);
}
- return start;
+ // SIGNATURE_START is just a prefix. Check that it is one of the known
+ // full signature start tags.
+ if (RawParseUtils.match(raw, start, GPG_SIGNATURE_START) > 0
+ || RawParseUtils.match(raw, start, CMS_SIGNATURE_START) > 0
+ || RawParseUtils.match(raw, start, SSH_SIGNATURE_START) > 0) {
+ return start;
+ }
+ return -1;
}
/**
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 76c14e9c26..41f98bad84 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -19,9 +19,14 @@ import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Optional;
+import java.util.Map;
+import java.util.
+Optional;
+import java.util.Set;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
@@ -31,9 +36,9 @@ import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.RevWalkException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.AsyncObjectLoaderQueue;
-import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -279,23 +284,6 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
}
/**
- * Get a reachability checker for commits over this revwalk.
- *
- * @return the most efficient reachability checker for this repository.
- * @throws IOException
- * if it cannot open any of the underlying indices.
- *
- * @since 5.4
- * @deprecated use {@code ObjectReader#createReachabilityChecker(RevWalk)}
- * instead.
- */
- @Deprecated
- public final ReachabilityChecker createReachabilityChecker()
- throws IOException {
- return reader.createReachabilityChecker(this);
- }
-
- /**
* {@inheritDoc}
* <p>
* Release any resources used by this walker's reader.
@@ -540,6 +528,27 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
}
/**
+ * Determine if a <code>commit</code> is merged into any of the given
+ * <code>revs</code>.
+ *
+ * @param commit
+ * commit the caller thinks is reachable from <code>revs</code>.
+ * @param revs
+ * commits to start iteration from, and which is most likely a
+ * descendant (child) of <code>commit</code>.
+ * @return true if commit is merged into any of the revs; false otherwise.
+ * @throws java.io.IOException
+ * a pack file or loose object could not be read.
+ * @since 6.10.1
+ */
+ public boolean isMergedIntoAnyCommit(RevCommit commit, Collection<RevCommit> revs)
+ throws IOException {
+ return getCommitsMergedInto(commit, revs,
+ GetMergedIntoStrategy.RETURN_ON_FIRST_FOUND,
+ NullProgressMonitor.INSTANCE).size() > 0;
+ }
+
+ /**
* Determine if a <code>commit</code> is merged into all of the given
* <code>refs</code>.
*
@@ -562,7 +571,26 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
private List<Ref> getMergedInto(RevCommit needle, Collection<Ref> haystacks,
Enum returnStrategy, ProgressMonitor monitor) throws IOException {
+ Map<RevCommit, List<Ref>> refsByCommit = new HashMap<>();
+ for (Ref r : haystacks) {
+ RevObject o = peel(parseAny(r.getObjectId()));
+ if (!(o instanceof RevCommit)) {
+ continue;
+ }
+ refsByCommit.computeIfAbsent((RevCommit) o, c -> new ArrayList<>()).add(r);
+ }
+ monitor.update(1);
List<Ref> result = new ArrayList<>();
+ for (RevCommit c : getCommitsMergedInto(needle, refsByCommit.keySet(),
+ returnStrategy, monitor)) {
+ result.addAll(refsByCommit.get(c));
+ }
+ return result;
+ }
+
+ private Set<RevCommit> getCommitsMergedInto(RevCommit needle, Collection<RevCommit> haystacks,
+ Enum returnStrategy, ProgressMonitor monitor) throws IOException {
+ Set<RevCommit> result = new HashSet<>();
List<RevCommit> uninteresting = new ArrayList<>();
List<RevCommit> marked = new ArrayList<>();
RevFilter oldRF = filter;
@@ -578,16 +606,11 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
needle.parseHeaders(this);
}
int cutoff = needle.getGeneration();
- for (Ref r : haystacks) {
+ for (RevCommit c : haystacks) {
if (monitor.isCancelled()) {
return result;
}
monitor.update(1);
- RevObject o = peel(parseAny(r.getObjectId()));
- if (!(o instanceof RevCommit)) {
- continue;
- }
- RevCommit c = (RevCommit) o;
reset(UNINTERESTING | TEMP_MARK);
markStart(c);
boolean commitFound = false;
@@ -599,7 +622,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
}
if (References.isSameObject(next, needle)
|| (next.flags & TEMP_MARK) != 0) {
- result.add(r);
+ result.add(c);
if (returnStrategy == GetMergedIntoStrategy.RETURN_ON_FIRST_FOUND) {
return result;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/CommitTimeRevFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/CommitTimeRevFilter.java
index 4100e877df..c9186b5491 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/CommitTimeRevFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/CommitTimeRevFilter.java
@@ -12,6 +12,7 @@
package org.eclipse.jgit.revwalk.filter;
import java.io.IOException;
+import java.time.Instant;
import java.util.Date;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -30,9 +31,24 @@ public abstract class CommitTimeRevFilter extends RevFilter {
* @param ts
* the point in time to cut on.
* @return a new filter to select commits on or before <code>ts</code>.
+ *
+ * @deprecated Use {@link #before(Instant)} instead.
*/
+ @Deprecated(since="7.2")
public static final RevFilter before(Date ts) {
- return before(ts.getTime());
+ return before(ts.toInstant());
+ }
+
+ /**
+ * Create a new filter to select commits before a given date/time.
+ *
+ * @param ts
+ * the point in time to cut on.
+ * @return a new filter to select commits on or before <code>ts</code>.
+ * @since 7.2
+ */
+ public static RevFilter before(Instant ts) {
+ return new Before(ts);
}
/**
@@ -43,7 +59,7 @@ public abstract class CommitTimeRevFilter extends RevFilter {
* @return a new filter to select commits on or before <code>ts</code>.
*/
public static final RevFilter before(long ts) {
- return new Before(ts);
+ return new Before(Instant.ofEpochMilli(ts));
}
/**
@@ -52,9 +68,24 @@ public abstract class CommitTimeRevFilter extends RevFilter {
* @param ts
* the point in time to cut on.
* @return a new filter to select commits on or after <code>ts</code>.
+ *
+ * @deprecated Use {@link #after(Instant)} instead.
*/
+ @Deprecated(since="7.2")
public static final RevFilter after(Date ts) {
- return after(ts.getTime());
+ return after(ts.toInstant());
+ }
+
+ /**
+ * Create a new filter to select commits after a given date/time.
+ *
+ * @param ts
+ * the point in time to cut on.
+ * @return a new filter to select commits on or after <code>ts</code>.
+ * @since 7.2
+ */
+ public static RevFilter after(Instant ts) {
+ return new After(ts);
}
/**
@@ -65,7 +96,7 @@ public abstract class CommitTimeRevFilter extends RevFilter {
* @return a new filter to select commits on or after <code>ts</code>.
*/
public static final RevFilter after(long ts) {
- return new After(ts);
+ return after(Instant.ofEpochMilli(ts));
}
/**
@@ -75,9 +106,28 @@ public abstract class CommitTimeRevFilter extends RevFilter {
* @param since the point in time to cut on.
* @param until the point in time to cut off.
* @return a new filter to select commits between the given date/times.
+ *
+ * @deprecated Use {@link #between(Instant, Instant)} instead.
*/
+ @Deprecated(since="7.2")
public static final RevFilter between(Date since, Date until) {
- return between(since.getTime(), until.getTime());
+ return between(since.toInstant(), until.toInstant());
+ }
+
+ /**
+ * Create a new filter to select commits after or equal a given date/time
+ * <code>since</code> and before or equal a given date/time
+ * <code>until</code>.
+ *
+ * @param since
+ * the point in time to cut on.
+ * @param until
+ * the point in time to cut off.
+ * @return a new filter to select commits between the given date/times.
+ * @since 7.2
+ */
+ public static RevFilter between(Instant since, Instant until) {
+ return new Between(since, until);
}
/**
@@ -87,9 +137,12 @@ public abstract class CommitTimeRevFilter extends RevFilter {
* @param since the point in time to cut on, in milliseconds.
* @param until the point in time to cut off, in millisconds.
* @return a new filter to select commits between the given date/times.
+ *
+ * @deprecated Use {@link #between(Instant, Instant)} instead.
*/
+ @Deprecated(since="7.2")
public static final RevFilter between(long since, long until) {
- return new Between(since, until);
+ return new Between(Instant.ofEpochMilli(since), Instant.ofEpochMilli(until));
}
final int when;
@@ -98,6 +151,10 @@ public abstract class CommitTimeRevFilter extends RevFilter {
when = (int) (ts / 1000);
}
+ CommitTimeRevFilter(Instant t) {
+ when = (int) t.getEpochSecond();
+ }
+
@Override
public RevFilter clone() {
return this;
@@ -109,8 +166,8 @@ public abstract class CommitTimeRevFilter extends RevFilter {
}
private static class Before extends CommitTimeRevFilter {
- Before(long ts) {
- super(ts);
+ Before(Instant t) {
+ super(t);
}
@Override
@@ -123,14 +180,12 @@ public abstract class CommitTimeRevFilter extends RevFilter {
@SuppressWarnings("nls")
@Override
public String toString() {
- return super.toString() + "(" + new Date(when * 1000L) + ")";
+ return super.toString() + "(" + Instant.ofEpochSecond(when) + ")";
}
}
private static class After extends CommitTimeRevFilter {
- After(long ts) {
- super(ts);
- }
+ After(Instant t) { super(t); }
@Override
public boolean include(RevWalk walker, RevCommit cmit)
@@ -148,16 +203,16 @@ public abstract class CommitTimeRevFilter extends RevFilter {
@SuppressWarnings("nls")
@Override
public String toString() {
- return super.toString() + "(" + new Date(when * 1000L) + ")";
+ return super.toString() + "(" + Instant.ofEpochSecond(when) + ")";
}
}
private static class Between extends CommitTimeRevFilter {
private final int until;
- Between(long since, long until) {
+ Between(Instant since, Instant until) {
super(since);
- this.until = (int) (until / 1000);
+ this.until = (int) until.getEpochSecond();
}
@Override
@@ -170,8 +225,8 @@ public abstract class CommitTimeRevFilter extends RevFilter {
@SuppressWarnings("nls")
@Override
public String toString() {
- return super.toString() + "(" + new Date(when * 1000L) + " - "
- + new Date(until * 1000L) + ")";
+ return super.toString() + "(" + Instant.ofEpochSecond(when) + " - "
+ + Instant.ofEpochSecond(until) + ")";
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java
index 7cb8618302..668b92cf53 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java
@@ -22,27 +22,6 @@ import org.eclipse.jgit.internal.storage.file.WindowCache;
*/
@MXBean
public interface WindowCacheStats {
- /**
- * Get number of open files
- *
- * @return the number of open files.
- * @deprecated use {@link #getOpenFileCount()} instead
- */
- @Deprecated
- public static int getOpenFiles() {
- return (int) WindowCache.getInstance().getStats().getOpenFileCount();
- }
-
- /**
- * Get number of open bytes
- *
- * @return the number of open bytes.
- * @deprecated use {@link #getOpenByteCount()} instead
- */
- @Deprecated
- public static long getOpenBytes() {
- return WindowCache.getInstance().getStats().getOpenByteCount();
- }
/**
* Get cache statistics
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
index 8373d6809a..863b79466a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
@@ -50,7 +50,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.zip.Deflater;
-import org.eclipse.jgit.internal.storage.file.PackIndexWriter;
+import org.eclipse.jgit.internal.storage.file.BasePackIndexWriter;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
@@ -995,7 +995,7 @@ public class PackConfig {
*
* @return the index version, the special version 0 designates the oldest
* (most compatible) format available for the objects.
- * @see PackIndexWriter
+ * @see BasePackIndexWriter
*/
public int getIndexVersion() {
return indexVersion;
@@ -1009,7 +1009,7 @@ public class PackConfig {
* @param version
* the version to write. The special version 0 designates the
* oldest (most compatible) format available for the objects.
- * @see PackIndexWriter
+ * @see BasePackIndexWriter
*/
public void setIndexVersion(int version) {
indexVersion = version;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
index becc8082ba..105cba7d28 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
@@ -787,14 +787,14 @@ public class SubmoduleWalk implements AutoCloseable {
IgnoreSubmoduleMode mode = repoConfig.getEnum(
IgnoreSubmoduleMode.values(),
ConfigConstants.CONFIG_SUBMODULE_SECTION, getModuleName(),
- ConfigConstants.CONFIG_KEY_IGNORE, null);
+ ConfigConstants.CONFIG_KEY_IGNORE);
if (mode != null) {
return mode;
}
lazyLoadModulesConfig();
- return modulesConfig.getEnum(IgnoreSubmoduleMode.values(),
- ConfigConstants.CONFIG_SUBMODULE_SECTION, getModuleName(),
- ConfigConstants.CONFIG_KEY_IGNORE, IgnoreSubmoduleMode.NONE);
+ return modulesConfig.getEnum(ConfigConstants.CONFIG_SUBMODULE_SECTION,
+ getModuleName(), ConfigConstants.CONFIG_KEY_IGNORE,
+ IgnoreSubmoduleMode.NONE);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
index b873925316..aaf9f8a08a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
@@ -757,8 +757,10 @@ public class AmazonS3 {
final XMLReader xr;
try {
- xr = SAXParserFactory.newInstance().newSAXParser()
- .getXMLReader();
+ SAXParserFactory saxParserFactory = SAXParserFactory
+ .newInstance();
+ saxParserFactory.setNamespaceAware(true);
+ xr = saxParserFactory.newSAXParser().getXMLReader();
} catch (SAXException | ParserConfigurationException e) {
throw new IOException(
JGitText.get().noXMLParserAvailable, e);
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 469a3d6015..be0d37b96e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
@@ -12,10 +12,10 @@
package org.eclipse.jgit.transport;
-import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DELIM;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_NOT;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_SINCE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DELIM;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DONE;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_END;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ERR;
@@ -32,7 +32,6 @@ import java.time.Instant;
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.List;
@@ -715,7 +714,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
// wind up later matching up against things we want and we
// can avoid asking for something we already happen to have.
//
- final Date maxWhen = new Date(maxTime * 1000L);
+ Instant maxWhen = Instant.ofEpochSecond(maxTime);
walk.sort(RevSort.COMMIT_TIME_DESC);
walk.markStart(reachableCommits);
walk.setRevFilter(CommitTimeRevFilter.after(maxWhen));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java
index 73eddb8e21..f10b7bf452 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java
@@ -302,8 +302,7 @@ public class HttpConfig {
int postBufferSize = config.getInt(HTTP, POST_BUFFER_KEY,
1 * 1024 * 1024);
boolean sslVerifyFlag = config.getBoolean(HTTP, SSL_VERIFY_KEY, true);
- HttpRedirectMode followRedirectsMode = config.getEnum(
- HttpRedirectMode.values(), HTTP, null,
+ HttpRedirectMode followRedirectsMode = config.getEnum(HTTP, null,
FOLLOW_REDIRECTS_KEY, HttpRedirectMode.INITIAL);
int redirectLimit = config.getInt(HTTP, MAX_REDIRECTS_KEY,
MAX_REDIRECTS);
@@ -335,8 +334,8 @@ public class HttpConfig {
postBufferSize);
sslVerifyFlag = config.getBoolean(HTTP, match, SSL_VERIFY_KEY,
sslVerifyFlag);
- followRedirectsMode = config.getEnum(HttpRedirectMode.values(),
- HTTP, match, FOLLOW_REDIRECTS_KEY, followRedirectsMode);
+ followRedirectsMode = config.getEnum(HTTP, match,
+ FOLLOW_REDIRECTS_KEY, followRedirectsMode);
int newMaxRedirects = config.getInt(HTTP, match, MAX_REDIRECTS_KEY,
redirectLimit);
if (newMaxRedirects >= 0) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
index 7224405df7..e1f2b19ce5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
@@ -530,7 +530,7 @@ public abstract class PackParser {
receiving.beginTask(JGitText.get().receivingObjects,
(int) expectedObjectCount);
try {
- for (int done = 0; done < expectedObjectCount; done++) {
+ for (long done = 0; done < expectedObjectCount; done++) {
indexOneObject();
receiving.update(1);
if (receiving.isCancelled())
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 ed33eaed07..614ad88246 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
@@ -43,24 +43,13 @@ public class PacketLineIn {
/**
* Magic return from {@link #readString()} when a flush packet is found.
- *
- * @deprecated Callers should use {@link #isEnd(String)} to check if a
- * string is the end marker, or
- * {@link PacketLineIn#readStrings()} to iterate over all
- * strings in the input stream until the marker is reached.
*/
- @Deprecated
- public static final String END = new String(); /* must not string pool */
+ private static final String END = new String(); /* must not string pool */
/**
* Magic return from {@link #readString()} when a delim packet is found.
- *
- * @since 5.0
- * @deprecated Callers should use {@link #isDelimiter(String)} to check if a
- * string is the delimiter.
*/
- @Deprecated
- public static final String DELIM = new String(); /* must not string pool */
+ private static final String DELIM = new String(); /* must not string pool */
enum AckNackResult {
/** NAK */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
index a9e93b6be6..6bdaf0e234 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
@@ -24,6 +24,7 @@ import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -329,7 +330,7 @@ public class PushCertificateStore implements AutoCloseable {
if (newId == null) {
return RefUpdate.Result.NO_CHANGE;
}
- try (ObjectInserter inserter = db.newObjectInserter()) {
+ try {
RefUpdate.Result result = updateRef(newId);
switch (result) {
case FAST_FORWARD:
@@ -404,8 +405,8 @@ public class PushCertificateStore implements AutoCloseable {
}
private static void sortPending(List<PendingCert> pending) {
- Collections.sort(pending, (PendingCert a, PendingCert b) -> Long.signum(
- a.ident.getWhen().getTime() - b.ident.getWhen().getTime()));
+ Collections.sort(pending,
+ Comparator.comparing((PendingCert a) -> a.ident.getWhenAsInstant()));
}
private DirCache newDirCache() throws IOException {
@@ -503,7 +504,7 @@ public class PushCertificateStore implements AutoCloseable {
} else {
sb.append(MessageFormat.format(
JGitText.get().storePushCertMultipleRefs,
- Integer.valueOf(cert.getCommands().size())));
+ cert.getCommands().size()));
}
return sb.append('\n').toString();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index ddde6038e9..6f211e0794 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -88,52 +88,6 @@ import org.eclipse.jgit.util.io.TimeoutOutputStream;
* Implements the server side of a push connection, receiving objects.
*/
public class ReceivePack {
- /**
- * Data in the first line of a request, the line itself plus capabilities.
- *
- * @deprecated Use {@link FirstCommand} instead.
- * @since 5.6
- */
- @Deprecated
- public static class FirstLine {
- private final FirstCommand command;
-
- /**
- * Parse the first line of a receive-pack request.
- *
- * @param line
- * line from the client.
- */
- public FirstLine(String line) {
- command = FirstCommand.fromLine(line);
- }
-
- /**
- * Get non-capabilities part of the line
- *
- * @return non-capabilities part of the line.
- */
- public String getLine() {
- return command.getLine();
- }
-
- /**
- * Get capabilities parsed from the line
- *
- * @return capabilities parsed from the line.
- */
- public Set<String> getCapabilities() {
- Set<String> reconstructedCapabilites = new HashSet<>();
- for (Map.Entry<String, String> e : command.getCapabilities()
- .entrySet()) {
- String cap = e.getValue() == null ? e.getKey()
- : e.getKey() + "=" + e.getValue(); //$NON-NLS-1$
- reconstructedCapabilites.add(cap);
- }
-
- return reconstructedCapabilites;
- }
- }
/** Database we write the stored objects into. */
private final Repository db;
@@ -2149,22 +2103,6 @@ public class ReceivePack {
}
/**
- * Set whether this class will report command failures as warning messages
- * before sending the command results.
- *
- * @param echo
- * if true this class will report command failures as warning
- * messages before sending the command results. This is usually
- * not necessary, but may help buggy Git clients that discard the
- * errors when all branches fail.
- * @deprecated no widely used Git versions need this any more
- */
- @Deprecated
- public void setEchoCommandFailures(boolean echo) {
- // No-op.
- }
-
- /**
* Get the client session-id
*
* @return The client session-id.
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 f72c421920..3d4bea2e48 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
@@ -178,7 +178,6 @@ public abstract class RefAdvertiser {
*
* This method must be invoked prior to any of the following:
* <ul>
- * <li>{@link #send(Map)}</li>
* <li>{@link #send(Collection)}</li>
* </ul>
*
@@ -195,7 +194,6 @@ public abstract class RefAdvertiser {
* <p>
* This method must be invoked prior to any of the following:
* <ul>
- * <li>{@link #send(Map)}</li>
* <li>{@link #send(Collection)}</li>
* <li>{@link #advertiseHave(AnyObjectId)}</li>
* </ul>
@@ -230,7 +228,6 @@ public abstract class RefAdvertiser {
* <p>
* This method must be invoked prior to any of the following:
* <ul>
- * <li>{@link #send(Map)}</li>
* <li>{@link #send(Collection)}</li>
* <li>{@link #advertiseHave(AnyObjectId)}</li>
* </ul>
@@ -260,24 +257,6 @@ public abstract class RefAdvertiser {
* @throws java.io.IOException
* the underlying output stream failed to write out an
* advertisement record.
- * @deprecated use {@link #send(Collection)} instead.
- */
- @Deprecated
- public Set<ObjectId> send(Map<String, Ref> refs) throws IOException {
- return send(refs.values());
- }
-
- /**
- * Format an advertisement for the supplied refs.
- *
- * @param refs
- * zero or more refs to format for the client. The collection is
- * sorted before display if necessary, and therefore may appear
- * in any order.
- * @return set of ObjectIds that were advertised to the client.
- * @throws java.io.IOException
- * the underlying output stream failed to write out an
- * advertisement record.
* @since 5.0
*/
public Set<ObjectId> send(Collection<Ref> refs) throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
index a0194ea8b1..8120df0698 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
@@ -11,8 +11,6 @@
package org.eclipse.jgit.transport;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.Iterator;
import java.util.ServiceLoader;
@@ -99,9 +97,8 @@ public abstract class SshSessionFactory {
* @since 5.2
*/
public static String getLocalUserName() {
- return AccessController
- .doPrivileged((PrivilegedAction<String>) () -> SystemReader
- .getInstance().getProperty(Constants.OS_USER_NAME_KEY));
+ return SystemReader.getInstance()
+ .getProperty(Constants.OS_USER_NAME_KEY);
}
/**
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 b335675da5..ac76e83d34 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -1121,28 +1121,6 @@ public abstract class Transport implements AutoCloseable {
}
/**
- * @return the blob limit value set with {@link #setFilterBlobLimit} or
- * {@link #setFilterSpec(FilterSpec)}, or -1 if no blob limit value
- * was set
- * @since 5.0
- * @deprecated Use {@link #getFilterSpec()} instead
- */
- @Deprecated
- public final long getFilterBlobLimit() {
- return filterSpec.getBlobLimit();
- }
-
- /**
- * @param bytes exclude blobs of size greater than this
- * @since 5.0
- * @deprecated Use {@link #setFilterSpec(FilterSpec)} instead
- */
- @Deprecated
- public final void setFilterBlobLimit(long bytes) {
- setFilterSpec(FilterSpec.withBlobLimit(bytes));
- }
-
- /**
* Get filter spec
*
* @return the last filter spec set with {@link #setFilterSpec(FilterSpec)},
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 0fc9710ecb..f77b04110d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
@@ -254,6 +254,12 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
pb.environment().put(Constants.GIT_DIR_KEY,
directory.getPath());
}
+ File commonDirectory = local != null ? local.getCommonDirectory()
+ : null;
+ if (commonDirectory != null) {
+ pb.environment().put(Constants.GIT_COMMON_DIR_KEY,
+ commonDirectory.getPath());
+ }
return pb;
}
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 3a06ce5b63..1b9431ce6e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
@@ -225,6 +225,7 @@ class TransportLocal extends Transport implements PackTransport {
env.remove("GIT_CONFIG"); //$NON-NLS-1$
env.remove("GIT_CONFIG_PARAMETERS"); //$NON-NLS-1$
env.remove("GIT_DIR"); //$NON-NLS-1$
+ env.remove("GIT_COMMON_DIR"); //$NON-NLS-1$
env.remove("GIT_WORK_TREE"); //$NON-NLS-1$
env.remove("GIT_GRAFT_FILE"); //$NON-NLS-1$
env.remove("GIT_INDEX_FILE"); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
index 4de6ff825f..7b5842b712 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
@@ -82,7 +82,7 @@ public class URIish implements Serializable {
* Part of a pattern which matches a relative path. Relative paths don't
* start with slash or drive letters. Defines no capturing group.
*/
- private static final String RELATIVE_PATH_P = "(?:(?:[^\\\\/]+[\\\\/]+)*[^\\\\/]+[\\\\/]*)"; //$NON-NLS-1$
+ private static final String RELATIVE_PATH_P = "(?:(?:[^\\\\/]+[\\\\/]+)*+[^\\\\/]*)"; //$NON-NLS-1$
/**
* Part of a pattern which matches a relative or absolute path. Defines no
@@ -120,7 +120,7 @@ public class URIish implements Serializable {
* path (maybe even containing windows drive-letters) or a relative path.
*/
private static final Pattern LOCAL_FILE = Pattern.compile("^" // //$NON-NLS-1$
- + "([\\\\/]?" + PATH_P + ")" // //$NON-NLS-1$ //$NON-NLS-2$
+ + "([\\\\/]?+" + PATH_P + ")" // //$NON-NLS-1$ //$NON-NLS-2$
+ "$"); //$NON-NLS-1$
/**
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 9318871520..41ab8acf05 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -30,11 +30,11 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK_D
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_DONE;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA;
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SESSION_ID;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_ALL;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
-import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SESSION_ID;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ACK;
@@ -80,7 +80,6 @@ import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.CachedPackUriProvider;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
-import org.eclipse.jgit.internal.transport.parser.FirstWant;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
@@ -118,13 +117,13 @@ public class UploadPack implements Closeable {
/** Policy the server uses to validate client requests */
public enum RequestPolicy {
/** Client may only ask for objects the server advertised a reference for. */
- ADVERTISED,
+ ADVERTISED(0x08),
/**
* Client may ask for any commit reachable from a reference advertised by
* the server.
*/
- REACHABLE_COMMIT,
+ REACHABLE_COMMIT(0x02),
/**
* Client may ask for objects that are the tip of any reference, even if not
@@ -134,18 +133,36 @@ public class UploadPack implements Closeable {
*
* @since 3.1
*/
- TIP,
+ TIP(0x01),
/**
* Client may ask for any commit reachable from any reference, even if that
- * reference wasn't advertised.
+ * reference wasn't advertised, implies REACHABLE_COMMIT and TIP.
*
* @since 3.1
*/
- REACHABLE_COMMIT_TIP,
+ REACHABLE_COMMIT_TIP(0x03),
+
+ /** Client may ask for any SHA-1 in the repository, implies REACHABLE_COMMIT_TIP. */
+ ANY(0x07);
+
+ private final int bitmask;
+
+ RequestPolicy(int bitmask) {
+ this.bitmask = bitmask;
+ }
- /** Client may ask for any SHA-1 in the repository. */
- ANY;
+ /**
+ * Check if the current policy implies another, based on its bitmask.
+ *
+ * @param implied
+ * the implied policy based on its bitmask.
+ * @return true if the policy is implied.
+ * @since 6.10.1
+ */
+ public boolean implies(RequestPolicy implied) {
+ return (bitmask & implied.bitmask) != 0;
+ }
}
/**
@@ -172,52 +189,6 @@ public class UploadPack implements Closeable {
throws PackProtocolException, IOException;
}
- /**
- * Data in the first line of a want-list, the line itself plus options.
- *
- * @deprecated Use {@link FirstWant} instead
- */
- @Deprecated
- public static class FirstLine {
-
- private final FirstWant firstWant;
-
- /**
- * @param line
- * line from the client.
- */
- public FirstLine(String line) {
- try {
- firstWant = FirstWant.fromLine(line);
- } catch (PackProtocolException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- /**
- * Get non-capabilities part of the line
- *
- * @return non-capabilities part of the line.
- */
- public String getLine() {
- return firstWant.getLine();
- }
-
- /**
- * Get capabilities parsed from the line
- *
- * @return capabilities parsed from the line.
- */
- public Set<String> getOptions() {
- if (firstWant.getAgent() != null) {
- Set<String> caps = new HashSet<>(firstWant.getCapabilities());
- caps.add(OPTION_AGENT + '=' + firstWant.getAgent());
- return caps;
- }
- return firstWant.getCapabilities();
- }
- }
-
/*
* {@link java.util.function.Consumer} doesn't allow throwing checked
* exceptions. Define our own to propagate IOExceptions.
@@ -1423,6 +1394,7 @@ public class UploadPack implements Closeable {
if (transferConfig.isAdvertiseObjectInfo()) {
caps.add(COMMAND_OBJECT_INFO);
}
+ caps.add(OPTION_AGENT + "=" + UserAgent.get());
return caps;
}
@@ -1629,13 +1601,9 @@ public class UploadPack implements Closeable {
if (!biDirectionalPipe)
adv.advertiseCapability(OPTION_NO_DONE);
RequestPolicy policy = getRequestPolicy();
- if (policy == RequestPolicy.TIP
- || policy == RequestPolicy.REACHABLE_COMMIT_TIP
- || policy == null)
+ if (policy == null || policy.implies(RequestPolicy.TIP))
adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
- if (policy == RequestPolicy.REACHABLE_COMMIT
- || policy == RequestPolicy.REACHABLE_COMMIT_TIP
- || policy == null)
+ if (policy == null || policy.implies(RequestPolicy.REACHABLE_COMMIT))
adv.advertiseCapability(OPTION_ALLOW_REACHABLE_SHA1_IN_WANT);
adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
if (transferConfig.isAllowFilter()) {
@@ -1693,18 +1661,6 @@ public class UploadPack implements Closeable {
}
/**
- * Deprecated synonym for {@code getFilterSpec().getBlobLimit()}.
- *
- * @return filter blob limit requested by the client, or -1 if no limit
- * @since 5.3
- * @deprecated Use {@link #getFilterSpec()} instead
- */
- @Deprecated
- public final long getFilterBlobLimit() {
- return getFilterSpec().getBlobLimit();
- }
-
- /**
* Returns the filter spec for the current request. Valid only after
* calling recvWants(). This may be a no-op filter spec, but it won't be
* null.
@@ -1996,10 +1952,9 @@ public class UploadPack implements Closeable {
@Override
public void checkWants(UploadPack up, List<ObjectId> wants)
throws PackProtocolException, IOException {
- if (!up.isBiDirectionalPipe())
+ if (!up.isBiDirectionalPipe() || !wants.isEmpty()) {
new ReachableCommitRequestValidator().checkWants(up, wants);
- else if (!wants.isEmpty())
- throw new WantNotValidException(wants.iterator().next());
+ }
}
}
@@ -2271,7 +2226,7 @@ public class UploadPack implements Closeable {
walk.resetRetain(SAVE);
walk.markStart((RevCommit) want);
if (oldestTime != 0)
- walk.setRevFilter(CommitTimeRevFilter.after(oldestTime * 1000L));
+ walk.setRevFilter(CommitTimeRevFilter.after(Instant.ofEpochSecond(oldestTime)));
for (;;) {
final RevCommit c = walk.next();
if (c == null)
@@ -2429,7 +2384,8 @@ public class UploadPack implements Closeable {
: req.getDepth() - 1;
pw.setShallowPack(req.getDepth(), unshallowCommits);
- // Ownership is transferred below
+ // dw borrows the reader from walk which is closed by #close
+ @SuppressWarnings("resource")
DepthWalk.RevWalk dw = new DepthWalk.RevWalk(
walk.getObjectReader(), walkDepth);
dw.setDeepenSince(req.getDeepenSince());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java
index 7b052ad4a7..b23ee97dcb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java
@@ -10,10 +10,6 @@
package org.eclipse.jgit.transport;
-import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
-
-import java.util.Set;
-
import org.eclipse.jgit.util.StringUtils;
/**
@@ -91,43 +87,6 @@ public class UserAgent {
userAgent = StringUtils.isEmptyOrNull(agent) ? null : clean(agent);
}
- /**
- *
- * @param options
- * options
- * @param transportAgent
- * name of transport agent
- * @return The transport agent.
- * @deprecated Capabilities with &lt;key&gt;=&lt;value&gt; shape are now
- * parsed alongside other capabilities in the ReceivePack flow.
- */
- @Deprecated
- static String getAgent(Set<String> options, String transportAgent) {
- if (options == null || options.isEmpty()) {
- return transportAgent;
- }
- for (String o : options) {
- if (o.startsWith(OPTION_AGENT)
- && o.length() > OPTION_AGENT.length()
- && o.charAt(OPTION_AGENT.length()) == '=') {
- return o.substring(OPTION_AGENT.length() + 1);
- }
- }
- return transportAgent;
- }
-
- /**
- *
- * @param options
- * options
- * @return True if the transport agent is set. False otherwise.
- * @deprecated Capabilities with &lt;key&gt;=&lt;value&gt; shape are now
- * parsed alongside other capabilities in the ReceivePack flow.
- */
- @Deprecated
- static boolean hasAgent(Set<String> options) {
- return getAgent(options, null) != null;
- }
private UserAgent() {
}
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 3da76f38fa..b7bb0cbce3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
@@ -22,8 +22,10 @@ import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.jgit.errors.CompoundException;
@@ -122,7 +124,7 @@ class WalkFetchConnection extends BaseFetchConnection {
private final Deque<WalkRemoteObjectDatabase> noAlternatesYet;
/** Packs we have discovered, but have not yet fetched locally. */
- private final Deque<RemotePack> unfetchedPacks;
+ private final Map<String, RemotePack> unfetchedPacks;
/**
* Packs whose indexes we have looked at in {@link #unfetchedPacks}.
@@ -164,7 +166,7 @@ class WalkFetchConnection extends BaseFetchConnection {
remotes = new ArrayList<>();
remotes.add(w);
- unfetchedPacks = new ArrayDeque<>();
+ unfetchedPacks = new LinkedHashMap<>();
packsConsidered = new HashSet<>();
noPacksYet = new ArrayDeque<>();
@@ -227,7 +229,7 @@ class WalkFetchConnection extends BaseFetchConnection {
public void close() {
inserter.close();
reader.close();
- for (RemotePack p : unfetchedPacks) {
+ for (RemotePack p : unfetchedPacks.values()) {
if (p.tmpIdx != null)
p.tmpIdx.delete();
}
@@ -422,8 +424,9 @@ class WalkFetchConnection extends BaseFetchConnection {
if (packNameList == null || packNameList.isEmpty())
continue;
for (String packName : packNameList) {
- if (packsConsidered.add(packName))
- unfetchedPacks.add(new RemotePack(wrr, packName));
+ if (packsConsidered.add(packName)) {
+ unfetchedPacks.put(packName, new RemotePack(wrr, packName));
+ }
}
if (downloadPackedObject(pm, id))
return;
@@ -466,15 +469,27 @@ class WalkFetchConnection extends BaseFetchConnection {
}
}
+ private boolean downloadPackedObject(ProgressMonitor monitor,
+ AnyObjectId id) throws TransportException {
+ Set<String> brokenPacks = new HashSet<>();
+ try {
+ return downloadPackedObject(monitor, id, brokenPacks);
+ } finally {
+ brokenPacks.forEach(unfetchedPacks::remove);
+ }
+ }
+
@SuppressWarnings("Finally")
private boolean downloadPackedObject(final ProgressMonitor monitor,
- final AnyObjectId id) throws TransportException {
+ final AnyObjectId id, Set<String> brokenPacks) throws TransportException {
// Search for the object in a remote pack whose index we have,
// but whose pack we do not yet have.
//
- final Iterator<RemotePack> packItr = unfetchedPacks.iterator();
- while (packItr.hasNext() && !monitor.isCancelled()) {
- final RemotePack pack = packItr.next();
+ for (Entry<String, RemotePack> entry : unfetchedPacks.entrySet()) {
+ if (monitor.isCancelled()) {
+ break;
+ }
+ final RemotePack pack = entry.getValue();
try {
pack.openIndex(monitor);
} catch (IOException err) {
@@ -484,7 +499,7 @@ class WalkFetchConnection extends BaseFetchConnection {
// another source, so don't consider it a failure.
//
recordError(id, err);
- packItr.remove();
+ brokenPacks.add(entry.getKey());
continue;
}
@@ -535,7 +550,7 @@ class WalkFetchConnection extends BaseFetchConnection {
}
throw new TransportException(e.getMessage(), e);
}
- packItr.remove();
+ brokenPacks.add(entry.getKey());
}
if (!alreadyHave(id)) {
@@ -550,11 +565,9 @@ class WalkFetchConnection extends BaseFetchConnection {
// Complete any other objects that we can.
//
- final Iterator<ObjectId> pending = swapFetchQueue();
- while (pending.hasNext()) {
- final ObjectId p = pending.next();
+ final Deque<ObjectId> pending = swapFetchQueue();
+ for (ObjectId p : pending) {
if (pack.index.hasObject(p)) {
- pending.remove();
process(p);
} else {
workQueue.add(p);
@@ -566,8 +579,8 @@ class WalkFetchConnection extends BaseFetchConnection {
return false;
}
- private Iterator<ObjectId> swapFetchQueue() {
- final Iterator<ObjectId> r = workQueue.iterator();
+ private Deque<ObjectId> swapFetchQueue() {
+ final Deque<ObjectId> r = workQueue;
workQueue = new ArrayDeque<>();
return r;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
index 125ee6cbe9..95b8221a8b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
@@ -36,29 +36,39 @@ import org.eclipse.jgit.annotations.NonNull;
*/
public interface HttpConnection {
/**
+ * HttpURLConnection#HTTP_OK
+ *
* @see HttpURLConnection#HTTP_OK
*/
int HTTP_OK = java.net.HttpURLConnection.HTTP_OK;
/**
+ * HttpURLConnection#HTTP_NOT_AUTHORITATIVE
+ *
* @see HttpURLConnection#HTTP_NOT_AUTHORITATIVE
* @since 5.8
*/
int HTTP_NOT_AUTHORITATIVE = java.net.HttpURLConnection.HTTP_NOT_AUTHORITATIVE;
/**
+ * HttpURLConnection#HTTP_MOVED_PERM
+ *
* @see HttpURLConnection#HTTP_MOVED_PERM
* @since 4.7
*/
int HTTP_MOVED_PERM = java.net.HttpURLConnection.HTTP_MOVED_PERM;
/**
+ * HttpURLConnection#HTTP_MOVED_TEMP
+ *
* @see HttpURLConnection#HTTP_MOVED_TEMP
* @since 4.9
*/
int HTTP_MOVED_TEMP = java.net.HttpURLConnection.HTTP_MOVED_TEMP;
/**
+ * HttpURLConnection#HTTP_SEE_OTHER
+ *
* @see HttpURLConnection#HTTP_SEE_OTHER
* @since 4.9
*/
@@ -85,16 +95,22 @@ public interface HttpConnection {
int HTTP_11_MOVED_PERM = 308;
/**
+ * HttpURLConnection#HTTP_NOT_FOUND
+ *
* @see HttpURLConnection#HTTP_NOT_FOUND
*/
int HTTP_NOT_FOUND = java.net.HttpURLConnection.HTTP_NOT_FOUND;
/**
+ * HttpURLConnection#HTTP_UNAUTHORIZED
+ *
* @see HttpURLConnection#HTTP_UNAUTHORIZED
*/
int HTTP_UNAUTHORIZED = java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
/**
+ * HttpURLConnection#HTTP_FORBIDDEN
+ *
* @see HttpURLConnection#HTTP_FORBIDDEN
*/
int HTTP_FORBIDDEN = java.net.HttpURLConnection.HTTP_FORBIDDEN;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
index 36fa72028e..0cac374844 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
@@ -371,12 +371,6 @@ public class FileTreeIterator extends WorkingTreeIterator {
return attributes.getLength();
}
- @Override
- @Deprecated
- public long getLastModified() {
- return attributes.getLastModifiedInstant().toEpochMilli();
- }
-
/**
* @since 5.1.9
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
index aaac2a72e0..31c216b4a8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -38,12 +38,12 @@ 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.CoreConfig.EolStreamType;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
@@ -1587,10 +1587,16 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
*/
private String getFilterCommandDefinition(String filterDriverName,
String filterCommandType) {
+ if (config == null) {
+ return null;
+ }
String key = filterDriverName + "." + filterCommandType; //$NON-NLS-1$
String filterCommand = filterCommandsByNameDotType.get(key);
if (filterCommand != null)
return filterCommand;
+ if (config == null) {
+ return null;
+ }
filterCommand = config.getString(ConfigConstants.CONFIG_FILTER_SECTION,
filterDriverName, filterCommandType);
boolean useBuiltin = config.getBoolean(
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 73a3ddaae7..f16d800f63 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -498,6 +498,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
filterProcessBuilder.directory(repository.getWorkTree());
filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
repository.getDirectory().getAbsolutePath());
+ filterProcessBuilder.environment().put(Constants.GIT_COMMON_DIR_KEY,
+ repository.getCommonDirectory().getAbsolutePath());
ExecutionResult result;
try {
result = fs.execute(filterProcessBuilder, in);
@@ -620,18 +622,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
/**
* Get the last modified time of this entry.
*
- * @return last modified time of this file, in milliseconds since the epoch
- * (Jan 1, 1970 UTC).
- * @deprecated use {@link #getEntryLastModifiedInstant()} instead
- */
- @Deprecated
- public long getEntryLastModified() {
- return current().getLastModified();
- }
-
- /**
- * Get the last modified time of this entry.
- *
* @return last modified time of this file
* @since 5.1.9
*/
@@ -1229,21 +1219,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
* needs to compute the value they should cache the reference within an
* instance member instead.
*
- * @return time since the epoch (in ms) of the last change.
- * @deprecated use {@link #getLastModifiedInstant()} instead
- */
- @Deprecated
- public abstract long getLastModified();
-
- /**
- * Get the last modified time of this entry.
- * <p>
- * <b>Note: Efficient implementation required.</b>
- * <p>
- * The implementation of this method must be efficient. If a subclass
- * needs to compute the value they should cache the reference within an
- * instance member instead.
- *
* @return time of the last change.
* @since 5.1.9
*/
@@ -1332,7 +1307,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
IgnoreNode infoExclude = new IgnoreNodeWithParent(
coreExclude);
- File exclude = fs.resolve(repository.getDirectory(),
+ File exclude = fs.resolve(repository.getCommonDirectory(),
Constants.INFO_EXCLUDE);
if (fs.exists(exclude)) {
loadRulesFromFile(infoExclude, exclude);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java
index bcf79a285d..33db6ea661 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java
@@ -13,12 +13,12 @@
package org.eclipse.jgit.treewalk.filter;
-import org.eclipse.jgit.util.RawParseUtils;
-
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
+import org.eclipse.jgit.util.RawParseUtils;
+
/**
* Specialized set for byte arrays, interpreted as strings for use in
* {@link PathFilterGroup.Group}. Most methods assume the hash is already know
@@ -141,13 +141,19 @@ class ByteArraySet {
}
/**
+ * Returns number of arrays in the set
+ *
* @return number of arrays in the set
*/
int size() {
return size;
}
- /** @return true if {@link #size()} is 0. */
+ /**
+ * Returns true if {@link #size()} is 0
+ *
+ * @return true if {@link #size()} is 0
+ */
boolean isEmpty() {
return size == 0;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
index 12af374b2e..c8421d6012 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
@@ -86,8 +86,8 @@ public class ChangeIdUtil {
}
}
- private static final Pattern issuePattern = Pattern
- .compile("^(Bug|Issue)[a-zA-Z0-9-]*:.*$"); //$NON-NLS-1$
+ private static final Pattern signedOffByPattern = Pattern
+ .compile("^Signed-off-by:.*$"); //$NON-NLS-1$
private static final Pattern footerPattern = Pattern
.compile("(^[a-zA-Z0-9-]+:(?!//).*$)"); //$NON-NLS-1$
@@ -159,7 +159,7 @@ public class ChangeIdUtil {
int footerFirstLine = indexOfFirstFooterLine(lines);
int insertAfter = footerFirstLine;
for (int i = footerFirstLine; i < lines.length; ++i) {
- if (issuePattern.matcher(lines[i]).matches()) {
+ if (!signedOffByPattern.matcher(lines[i]).matches()) {
insertAfter = i + 1;
continue;
}
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 a8e1dae10e..59bbacfa76 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -30,7 +30,6 @@ import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
-import java.security.AccessControlException;
import java.text.MessageFormat;
import java.time.Duration;
import java.time.Instant;
@@ -262,31 +261,6 @@ public abstract class FS {
private static final AtomicInteger threadNumber = new AtomicInteger(1);
/**
- * Don't use the default thread factory of the ForkJoinPool for the
- * CompletableFuture; it runs without any privileges, which causes
- * trouble if a SecurityManager is present.
- * <p>
- * Instead use normal daemon threads. They'll belong to the
- * SecurityManager's thread group, or use the one of the calling thread,
- * as appropriate.
- * </p>
- *
- * @see java.util.concurrent.Executors#newCachedThreadPool()
- */
- private static final ExecutorService FUTURE_RUNNER = new ThreadPoolExecutor(
- 5, 5, 30L, TimeUnit.SECONDS,
- new LinkedBlockingQueue<>(),
- runnable -> {
- Thread t = new Thread(runnable,
- "JGit-FileStoreAttributeReader-" //$NON-NLS-1$
- + threadNumber.getAndIncrement());
- // Make sure these threads don't prevent application/JVM
- // shutdown.
- t.setDaemon(true);
- return t;
- });
-
- /**
* Use a separate executor with at most one thread to synchronize
* writing to the config. We write asynchronously since the config
* itself might be on a different file system, which might otherwise
@@ -463,7 +437,7 @@ public abstract class FS {
locks.remove(s);
}
return attributes;
- }, FUTURE_RUNNER);
+ });
f = f.exceptionally(e -> {
LOG.error(e.getLocalizedMessage(), e);
return Optional.empty();
@@ -898,21 +872,6 @@ public abstract class FS {
}
/**
- * Whether FileStore attributes should be determined asynchronously
- *
- * @param asynch
- * whether FileStore attributes should be determined
- * asynchronously. If false access to cached attributes may block
- * for some seconds for the first call per FileStore
- * @since 5.1.9
- * @deprecated Use {@link FileStoreAttributes#setBackground} instead
- */
- @Deprecated
- public static void setAsyncFileStoreAttributes(boolean asynch) {
- FileStoreAttributes.setBackground(asynch);
- }
-
- /**
* Auto-detect the appropriate file system abstraction, taking into account
* the presence of a Cygwin installation on the system. Using jgit in
* combination with Cygwin requires a more elaborate (and possibly slower)
@@ -1085,24 +1044,6 @@ public abstract class FS {
* symbolic links, the modification time of the link is returned, rather
* than that of the link target.
*
- * @param f
- * a {@link java.io.File} object.
- * @return last modified time of f
- * @throws java.io.IOException
- * if an IO error occurred
- * @since 3.0
- * @deprecated use {@link #lastModifiedInstant(Path)} instead
- */
- @Deprecated
- public long lastModified(File f) throws IOException {
- return FileUtils.lastModified(f);
- }
-
- /**
- * Get the last modified time of a file system object. If the OS/JRE support
- * symbolic links, the modification time of the link is returned, rather
- * than that of the link target.
- *
* @param p
* a {@link Path} object.
* @return last modified time of p
@@ -1131,25 +1072,6 @@ public abstract class FS {
* <p>
* For symlinks it sets the modified time of the link target.
*
- * @param f
- * a {@link java.io.File} object.
- * @param time
- * last modified time
- * @throws java.io.IOException
- * if an IO error occurred
- * @since 3.0
- * @deprecated use {@link #setLastModified(Path, Instant)} instead
- */
- @Deprecated
- public void setLastModified(File f, long time) throws IOException {
- FileUtils.setLastModified(f, time);
- }
-
- /**
- * Set the last modified time of a file system object.
- * <p>
- * For symlinks it sets the modified time of the link target.
- *
* @param p
* a {@link Path} object.
* @param time
@@ -1443,13 +1365,6 @@ public abstract class FS {
}
} catch (IOException e) {
LOG.error("Caught exception in FS.readPipe()", e); //$NON-NLS-1$
- } catch (AccessControlException e) {
- LOG.warn(MessageFormat.format(
- JGitText.get().readPipeIsNotAllowedRequiredPermission,
- command, dir, e.getPermission()));
- } catch (SecurityException e) {
- LOG.warn(MessageFormat.format(JGitText.get().readPipeIsNotAllowed,
- command, dir));
}
if (debug) {
LOG.debug("readpipe returns null"); //$NON-NLS-1$
@@ -1800,25 +1715,6 @@ public abstract class FS {
}
/**
- * Create a new file. See {@link java.io.File#createNewFile()}. Subclasses
- * of this class may take care to provide a safe implementation for this
- * even if {@link #supportsAtomicCreateNewFile()} is <code>false</code>
- *
- * @param path
- * the file to be created
- * @return <code>true</code> if the file was created, <code>false</code> if
- * the file already existed
- * @throws java.io.IOException
- * if an IO error occurred
- * @deprecated use {@link #createNewFileAtomic(File)} instead
- * @since 4.5
- */
- @Deprecated
- public boolean createNewFile(File path) throws IOException {
- return path.createNewFile();
- }
-
- /**
* A token representing a file created by
* {@link #createNewFileAtomic(File)}. The token must be retained until the
* file has been deleted in order to guarantee that the unique file was
@@ -2042,6 +1938,8 @@ public abstract class FS {
environment.put(Constants.GIT_DIR_KEY,
repository.getDirectory().getAbsolutePath());
if (!repository.isBare()) {
+ environment.put(Constants.GIT_COMMON_DIR_KEY,
+ repository.getCommonDirectory().getAbsolutePath());
environment.put(Constants.GIT_WORK_TREE_KEY,
repository.getWorkTree().getAbsolutePath());
}
@@ -2137,7 +2035,7 @@ public abstract class FS {
case "post-receive": //$NON-NLS-1$
case "post-update": //$NON-NLS-1$
case "push-to-checkout": //$NON-NLS-1$
- return repository.getDirectory();
+ return repository.getCommonDirectory();
default:
return repository.getWorkTree();
}
@@ -2150,7 +2048,7 @@ public abstract class FS {
if (hooksDir != null) {
return new File(hooksDir);
}
- File dir = repository.getDirectory();
+ File dir = repository.getCommonDirectory();
return dir == null ? null : new File(dir, Constants.HOOKS);
}
@@ -2424,19 +2322,6 @@ public abstract class FS {
}
/**
- * Get the time when the file was last modified in milliseconds since
- * the epoch
- *
- * @return the time (milliseconds since 1970-01-01) when this object was
- * last modified
- * @deprecated use getLastModifiedInstant instead
- */
- @Deprecated
- public long getLastModifiedTime() {
- return lastModifiedInstant.toEpochMilli();
- }
-
- /**
* Get the time when this object was last modified
*
* @return the time when this object was last modified
@@ -2578,6 +2463,33 @@ public abstract class FS {
}
/**
+ * Get common dir path.
+ *
+ * @param dir
+ * the .git folder
+ * @return common dir path
+ * @throws IOException
+ * if commondir file can't be read
+ *
+ * @since 7.0
+ */
+ public File getCommonDir(File dir) throws IOException {
+ // first the GIT_COMMON_DIR is same as GIT_DIR
+ File commonDir = dir;
+ // now check if commondir file exists (e.g. worktree repository)
+ File commonDirFile = new File(dir, Constants.COMMONDIR_FILE);
+ if (commonDirFile.isFile()) {
+ String commonDirPath = new String(IO.readFully(commonDirFile))
+ .trim();
+ commonDir = new File(commonDirPath);
+ if (!commonDir.isAbsolute()) {
+ commonDir = new File(dir, commonDirPath).getCanonicalFile();
+ }
+ }
+ return commonDir;
+ }
+
+ /**
* This runnable will consume an input stream's content into an output
* stream as soon as it gets available.
* <p>
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 ee907f2ee6..db2b5b4f71 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Robin Rosenberg and others
+ * Copyright (C) 2010, 2024, 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
@@ -203,7 +203,16 @@ public class FS_POSIX extends FS {
/** {@inheritDoc} */
@Override
public boolean canExecute(File f) {
- return FileUtils.canExecute(f);
+ if (!isFile(f)) {
+ return false;
+ }
+ try {
+ Path path = FileUtils.toPath(f);
+ Set<PosixFilePermission> pset = Files.getPosixFilePermissions(path);
+ return pset.contains(PosixFilePermission.OWNER_EXECUTE);
+ } catch (IOException ex) {
+ return false;
+ }
}
/** {@inheritDoc} */
@@ -332,73 +341,6 @@ public class FS_POSIX extends FS {
return supportsAtomicFileCreation == AtomicFileCreation.SUPPORTED;
}
- @Override
- @SuppressWarnings("boxing")
- /**
- * {@inheritDoc}
- * <p>
- * An implementation of the File#createNewFile() semantics which works also
- * on NFS. If the config option
- * {@code core.supportsAtomicCreateNewFile = true} (which is the default)
- * then simply File#createNewFile() is called.
- *
- * But if {@code core.supportsAtomicCreateNewFile = false} then after
- * successful creation of the lock file a hard link to that lock file is
- * created and the attribute nlink of the lock file is checked to be 2. If
- * multiple clients manage to create the same lock file nlink would be
- * greater than 2 showing the error.
- *
- * @see "https://www.time-travellers.org/shane/papers/NFS_considered_harmful.html"
- *
- * @deprecated use {@link FS_POSIX#createNewFileAtomic(File)} instead
- * @since 4.5
- */
- @Deprecated
- public boolean createNewFile(File lock) throws IOException {
- if (!lock.createNewFile()) {
- return false;
- }
- if (supportsAtomicCreateNewFile()) {
- return true;
- }
- Path lockPath = lock.toPath();
- Path link = null;
- FileStore store = null;
- try {
- store = Files.getFileStore(lockPath);
- } catch (SecurityException e) {
- return true;
- }
- try {
- Boolean canLink = CAN_HARD_LINK.computeIfAbsent(store,
- s -> Boolean.TRUE);
- if (Boolean.FALSE.equals(canLink)) {
- return true;
- }
- link = Files.createLink(
- Paths.get(lock.getAbsolutePath() + ".lnk"), //$NON-NLS-1$
- lockPath);
- Integer nlink = (Integer) Files.getAttribute(lockPath,
- "unix:nlink"); //$NON-NLS-1$
- if (nlink > 2) {
- LOG.warn(MessageFormat.format(
- JGitText.get().failedAtomicFileCreation, lockPath,
- nlink));
- return false;
- } else if (nlink < 2) {
- CAN_HARD_LINK.put(store, Boolean.FALSE);
- }
- return true;
- } catch (UnsupportedOperationException | IllegalArgumentException e) {
- CAN_HARD_LINK.put(store, Boolean.FALSE);
- return true;
- } finally {
- if (link != null) {
- Files.delete(link);
- }
- }
- }
-
/**
* {@inheritDoc}
* <p>
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 635351ac84..237879110a 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
@@ -14,8 +14,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.File;
import java.io.OutputStream;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -43,10 +41,7 @@ public class FS_Win32_Cygwin extends FS_Win32 {
* @return true if cygwin is found
*/
public static boolean isCygwin() {
- final String path = AccessController
- .doPrivileged((PrivilegedAction<String>) () -> System
- .getProperty("java.library.path") //$NON-NLS-1$
- );
+ final String path = System.getProperty("java.library.path"); //$NON-NLS-1$
if (path == null)
return false;
File found = FS.searchPath(path, "cygpath.exe"); //$NON-NLS-1$
@@ -99,9 +94,7 @@ public class FS_Win32_Cygwin extends FS_Win32 {
@Override
protected File userHomeImpl() {
- final String home = AccessController.doPrivileged(
- (PrivilegedAction<String>) () -> System.getenv("HOME") //$NON-NLS-1$
- );
+ final String home = System.getenv("HOME"); //$NON-NLS-1$
if (home == null || home.length() == 0)
return super.userHomeImpl();
return resolve(new File("."), home); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index cab0e6a0a9..39c67f1b86 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -771,24 +771,6 @@ public class FileUtils {
}
/**
- * Get the lastModified attribute for a given file
- *
- * @param file
- * the file
- * @return lastModified attribute for given file, not following symbolic
- * links
- * @throws IOException
- * if an IO error occurred
- * @deprecated use {@link #lastModifiedInstant(Path)} instead which returns
- * FileTime
- */
- @Deprecated
- static long lastModified(File file) throws IOException {
- return Files.getLastModifiedTime(toPath(file), LinkOption.NOFOLLOW_LINKS)
- .toMillis();
- }
-
- /**
* Get last modified timestamp of a file
*
* @param path
@@ -830,21 +812,6 @@ public class FileUtils {
/**
* Set the last modified time of a file system object.
*
- * @param file
- * the file
- * @param time
- * last modified timestamp
- * @throws IOException
- * if an IO error occurred
- */
- @Deprecated
- static void setLastModified(File file, long time) throws IOException {
- Files.setLastModifiedTime(toPath(file), FileTime.fromMillis(time));
- }
-
- /**
- * Set the last modified time of a file system object.
- *
* @param path
* file path
* @param time
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java
index e6bf497ac4..332e65985e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java
@@ -10,10 +10,10 @@
package org.eclipse.jgit.util;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
import java.util.Locale;
-import java.util.TimeZone;
import org.eclipse.jgit.lib.PersonIdent;
@@ -26,9 +26,9 @@ import org.eclipse.jgit.lib.PersonIdent;
*/
public class GitDateFormatter {
- private DateFormat dateTimeInstance;
+ private DateTimeFormatter dateTimeFormat;
- private DateFormat dateTimeInstance2;
+ private DateTimeFormatter dateTimeFormat2;
private final Format format;
@@ -96,30 +96,34 @@ public class GitDateFormatter {
default:
break;
case DEFAULT: // Not default:
- dateTimeInstance = new SimpleDateFormat(
+ dateTimeFormat = DateTimeFormatter.ofPattern(
"EEE MMM dd HH:mm:ss yyyy Z", Locale.US); //$NON-NLS-1$
break;
case ISO:
- dateTimeInstance = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z", //$NON-NLS-1$
+ dateTimeFormat = DateTimeFormatter.ofPattern(
+ "yyyy-MM-dd HH:mm:ss Z", //$NON-NLS-1$
Locale.US);
break;
case LOCAL:
- dateTimeInstance = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", //$NON-NLS-1$
+ dateTimeFormat = DateTimeFormatter.ofPattern(
+ "EEE MMM dd HH:mm:ss yyyy", //$NON-NLS-1$
Locale.US);
break;
case RFC:
- dateTimeInstance = new SimpleDateFormat(
+ dateTimeFormat = DateTimeFormatter.ofPattern(
"EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); //$NON-NLS-1$
break;
case SHORT:
- dateTimeInstance = new SimpleDateFormat("yyyy-MM-dd", Locale.US); //$NON-NLS-1$
+ dateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd", //$NON-NLS-1$
+ Locale.US);
break;
case LOCALE:
case LOCALELOCAL:
- SystemReader systemReader = SystemReader.getInstance();
- dateTimeInstance = systemReader.getDateTimeInstance(
- DateFormat.DEFAULT, DateFormat.DEFAULT);
- dateTimeInstance2 = systemReader.getSimpleDateFormat("Z"); //$NON-NLS-1$
+ dateTimeFormat = DateTimeFormatter
+ .ofLocalizedDateTime(FormatStyle.MEDIUM)
+ .withLocale(Locale.US);
+ dateTimeFormat2 = DateTimeFormatter.ofPattern("Z", //$NON-NLS-1$
+ Locale.US);
break;
}
}
@@ -135,39 +139,45 @@ public class GitDateFormatter {
@SuppressWarnings("boxing")
public String formatDate(PersonIdent ident) {
switch (format) {
- case RAW:
- int offset = ident.getTimeZoneOffset();
+ case RAW: {
+ int offset = ident.getZoneOffset().getTotalSeconds();
String sign = offset < 0 ? "-" : "+"; //$NON-NLS-1$ //$NON-NLS-2$
int offset2;
- if (offset < 0)
+ if (offset < 0) {
offset2 = -offset;
- else
+ } else {
offset2 = offset;
- int hours = offset2 / 60;
- int minutes = offset2 % 60;
+ }
+ int minutes = (offset2 / 60) % 60;
+ int hours = offset2 / 60 / 60;
return String.format("%d %s%02d%02d", //$NON-NLS-1$
- ident.getWhen().getTime() / 1000, sign, hours, minutes);
+ ident.getWhenAsInstant().getEpochSecond(), sign, hours,
+ minutes);
+ }
case RELATIVE:
- return RelativeDateFormatter.format(ident.getWhen());
+ return RelativeDateFormatter.format(ident.getWhenAsInstant());
case LOCALELOCAL:
case LOCAL:
- dateTimeInstance.setTimeZone(SystemReader.getInstance()
- .getTimeZone());
- return dateTimeInstance.format(ident.getWhen());
- case LOCALE:
- TimeZone tz = ident.getTimeZone();
- if (tz == null)
- tz = SystemReader.getInstance().getTimeZone();
- dateTimeInstance.setTimeZone(tz);
- dateTimeInstance2.setTimeZone(tz);
- return dateTimeInstance.format(ident.getWhen()) + " " //$NON-NLS-1$
- + dateTimeInstance2.format(ident.getWhen());
- default:
- tz = ident.getTimeZone();
- if (tz == null)
- tz = SystemReader.getInstance().getTimeZone();
- dateTimeInstance.setTimeZone(ident.getTimeZone());
- return dateTimeInstance.format(ident.getWhen());
+ return dateTimeFormat
+ .withZone(SystemReader.getInstance().getTimeZoneId())
+ .format(ident.getWhenAsInstant());
+ case LOCALE: {
+ ZoneId tz = ident.getZoneId();
+ if (tz == null) {
+ tz = SystemReader.getInstance().getTimeZoneId();
+ }
+ return dateTimeFormat.withZone(tz).format(ident.getWhenAsInstant())
+ + " " //$NON-NLS-1$
+ + dateTimeFormat2.withZone(tz)
+ .format(ident.getWhenAsInstant());
+ }
+ default: {
+ ZoneId tz = ident.getZoneId();
+ if (tz == null) {
+ tz = SystemReader.getInstance().getTimeZoneId();
+ }
+ return dateTimeFormat.withZone(tz).format(ident.getWhenAsInstant());
+ }
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
index 6a4b39652a..f080056546 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
@@ -28,7 +28,10 @@ import org.eclipse.jgit.internal.JGitText;
* used. One example is the parsing of the config parameter gc.pruneexpire. The
* parser can handle only subset of what native gits approxidate parser
* understands.
+ *
+ * @deprecated Use {@link GitTimeParser} instead.
*/
+@Deprecated(since = "7.1")
public class GitDateParser {
/**
* The Date representing never. Though this is a concrete value, most
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitTimeParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitTimeParser.java
new file mode 100644
index 0000000000..acaa1ce563
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitTimeParser.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2024 Christian Halstrick 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.text.ParseException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.util.EnumMap;
+import java.util.Map;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.JGitText;
+
+/**
+ * Parses strings with time and date specifications into
+ * {@link java.time.Instant}.
+ *
+ * When git needs to parse strings specified by the user this parser can be
+ * used. One example is the parsing of the config parameter gc.pruneexpire. The
+ * parser can handle only subset of what native gits approxidate parser
+ * understands.
+ *
+ * @since 7.1
+ */
+public class GitTimeParser {
+
+ private static final Map<ParseableSimpleDateFormat, DateTimeFormatter> formatCache = new EnumMap<>(
+ ParseableSimpleDateFormat.class);
+
+ // An enum of all those formats which this parser can parse with the help of
+ // a DateTimeFormatter. There are other formats (e.g. the relative formats
+ // like "yesterday" or "1 week ago") which this parser can parse but which
+ // are not listed here because they are parsed without the help of a
+ // DateTimeFormatter.
+ enum ParseableSimpleDateFormat {
+ ISO("yyyy-MM-dd HH:mm:ss Z"), // //$NON-NLS-1$
+ RFC("EEE, dd MMM yyyy HH:mm:ss Z"), // //$NON-NLS-1$
+ SHORT("yyyy-MM-dd"), // //$NON-NLS-1$
+ SHORT_WITH_DOTS_REVERSE("dd.MM.yyyy"), // //$NON-NLS-1$
+ SHORT_WITH_DOTS("yyyy.MM.dd"), // //$NON-NLS-1$
+ SHORT_WITH_SLASH("MM/dd/yyyy"), // //$NON-NLS-1$
+ DEFAULT("EEE MMM dd HH:mm:ss yyyy Z"), // //$NON-NLS-1$
+ LOCAL("EEE MMM dd HH:mm:ss yyyy"); //$NON-NLS-1$
+
+ private final String formatStr;
+
+ ParseableSimpleDateFormat(String formatStr) {
+ this.formatStr = formatStr;
+ }
+ }
+
+ private GitTimeParser() {
+ // This class is not supposed to be instantiated
+ }
+
+ /**
+ * Parses a string into a {@link java.time.LocalDateTime} using the default
+ * locale. Since this parser also supports relative formats (e.g.
+ * "yesterday") the caller can specify the reference date. These types of
+ * strings can be parsed:
+ * <ul>
+ * <li>"never"</li>
+ * <li>"now"</li>
+ * <li>"yesterday"</li>
+ * <li>"(x) years|months|weeks|days|hours|minutes|seconds ago"<br>
+ * Multiple specs can be combined like in "2 weeks 3 days ago". Instead of '
+ * ' one can use '.' to separate the words</li>
+ * <li>"yyyy-MM-dd HH:mm:ss Z" (ISO)</li>
+ * <li>"EEE, dd MMM yyyy HH:mm:ss Z" (RFC)</li>
+ * <li>"yyyy-MM-dd"</li>
+ * <li>"yyyy.MM.dd"</li>
+ * <li>"MM/dd/yyyy",</li>
+ * <li>"dd.MM.yyyy"</li>
+ * <li>"EEE MMM dd HH:mm:ss yyyy Z" (DEFAULT)</li>
+ * <li>"EEE MMM dd HH:mm:ss yyyy" (LOCAL)</li>
+ * </ul>
+ *
+ * @param dateStr
+ * the string to be parsed
+ * @return the parsed {@link java.time.LocalDateTime}
+ * @throws java.text.ParseException
+ * if the given dateStr was not recognized
+ */
+ public static LocalDateTime parse(String dateStr) throws ParseException {
+ return parse(dateStr, SystemReader.getInstance().civilNow());
+ }
+
+ /**
+ * Parses a string into a {@link java.time.Instant} using the default
+ * locale. Since this parser also supports relative formats (e.g.
+ * "yesterday") the caller can specify the reference date. These types of
+ * strings can be parsed:
+ * <ul>
+ * <li>"never"</li>
+ * <li>"now"</li>
+ * <li>"yesterday"</li>
+ * <li>"(x) years|months|weeks|days|hours|minutes|seconds ago"<br>
+ * Multiple specs can be combined like in "2 weeks 3 days ago". Instead of '
+ * ' one can use '.' to separate the words</li>
+ * <li>"yyyy-MM-dd HH:mm:ss Z" (ISO)</li>
+ * <li>"EEE, dd MMM yyyy HH:mm:ss Z" (RFC)</li>
+ * <li>"yyyy-MM-dd"</li>
+ * <li>"yyyy.MM.dd"</li>
+ * <li>"MM/dd/yyyy",</li>
+ * <li>"dd.MM.yyyy"</li>
+ * <li>"EEE MMM dd HH:mm:ss yyyy Z" (DEFAULT)</li>
+ * <li>"EEE MMM dd HH:mm:ss yyyy" (LOCAL)</li>
+ * </ul>
+ *
+ * @param dateStr
+ * the string to be parsed
+ * @return the parsed {@link java.time.Instant}
+ * @throws java.text.ParseException
+ * if the given dateStr was not recognized
+ * @since 7.2
+ */
+ public static Instant parseInstant(String dateStr) throws ParseException {
+ return parse(dateStr).atZone(SystemReader.getInstance().getTimeZoneId())
+ .toInstant();
+ }
+
+ // Only tests seem to use this method
+ static LocalDateTime parse(String dateStr, LocalDateTime now)
+ throws ParseException {
+ dateStr = dateStr.trim();
+
+ if (dateStr.equalsIgnoreCase("never")) { //$NON-NLS-1$
+ return LocalDateTime.MAX;
+ }
+ LocalDateTime ret = parseRelative(dateStr, now);
+ if (ret != null) {
+ return ret;
+ }
+ for (ParseableSimpleDateFormat f : ParseableSimpleDateFormat.values()) {
+ try {
+ return parseSimple(dateStr, f);
+ } catch (DateTimeParseException e) {
+ // simply proceed with the next parser
+ }
+ }
+ ParseableSimpleDateFormat[] values = ParseableSimpleDateFormat.values();
+ StringBuilder allFormats = new StringBuilder("\"") //$NON-NLS-1$
+ .append(values[0].formatStr);
+ for (int i = 1; i < values.length; i++) {
+ allFormats.append("\", \"").append(values[i].formatStr); //$NON-NLS-1$
+ }
+ allFormats.append("\""); //$NON-NLS-1$
+ throw new ParseException(
+ MessageFormat.format(JGitText.get().cannotParseDate, dateStr,
+ allFormats.toString()),
+ 0);
+ }
+
+ // tries to parse a string with the formats supported by DateTimeFormatter
+ private static LocalDateTime parseSimple(String dateStr,
+ ParseableSimpleDateFormat f) throws DateTimeParseException {
+ DateTimeFormatter dateFormat = formatCache.computeIfAbsent(f,
+ format -> DateTimeFormatter
+ .ofPattern(f.formatStr)
+ .withLocale(SystemReader.getInstance().getLocale()));
+ TemporalAccessor parsed = dateFormat.parse(dateStr);
+ return parsed.isSupported(ChronoField.HOUR_OF_DAY)
+ ? LocalDateTime.from(parsed)
+ : LocalDate.from(parsed).atStartOfDay();
+ }
+
+ // tries to parse a string with a relative time specification
+ @SuppressWarnings("nls")
+ @Nullable
+ private static LocalDateTime parseRelative(String dateStr,
+ LocalDateTime now) {
+ // check for the static words "yesterday" or "now"
+ if (dateStr.equals("now")) {
+ return now;
+ }
+
+ if (dateStr.equals("yesterday")) {
+ return now.minusDays(1);
+ }
+
+ // parse constructs like "3 days ago", "5.week.2.day.ago"
+ String[] parts = dateStr.split("\\.| ", -1);
+ int partsLength = parts.length;
+ // check we have an odd number of parts (at least 3) and that the last
+ // part is "ago"
+ if (partsLength < 3 || (partsLength & 1) == 0
+ || !parts[parts.length - 1].equals("ago")) {
+ return null;
+ }
+ int number;
+ for (int i = 0; i < parts.length - 2; i += 2) {
+ try {
+ number = Integer.parseInt(parts[i]);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ if (parts[i + 1] == null) {
+ return null;
+ }
+ switch (parts[i + 1]) {
+ case "year":
+ case "years":
+ now = now.minusYears(number);
+ break;
+ case "month":
+ case "months":
+ now = now.minusMonths(number);
+ break;
+ case "week":
+ case "weeks":
+ now = now.minusWeeks(number);
+ break;
+ case "day":
+ case "days":
+ now = now.minusDays(number);
+ break;
+ case "hour":
+ case "hours":
+ now = now.minusHours(number);
+ break;
+ case "minute":
+ case "minutes":
+ now = now.minusMinutes(number);
+ break;
+ case "second":
+ case "seconds":
+ now = now.minusSeconds(number);
+ break;
+ default:
+ return null;
+ }
+ }
+ return now;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
index 46d0bc85ff..3ed72516c7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
@@ -13,6 +13,8 @@ package org.eclipse.jgit.util;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.time.Instant.EPOCH;
+import static java.time.ZoneOffset.UTC;
import static org.eclipse.jgit.lib.ObjectChecker.author;
import static org.eclipse.jgit.lib.ObjectChecker.committer;
import static org.eclipse.jgit.lib.ObjectChecker.encoding;
@@ -30,6 +32,10 @@ import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -44,14 +50,6 @@ import org.eclipse.jgit.lib.PersonIdent;
* Handy utility functions to parse raw object contents.
*/
public final class RawParseUtils {
- /**
- * UTF-8 charset constant.
- *
- * @since 2.2
- * @deprecated use {@link java.nio.charset.StandardCharsets#UTF_8} instead
- */
- @Deprecated
- public static final Charset UTF8_CHARSET = UTF_8;
private static final byte[] digits10;
@@ -467,6 +465,29 @@ public final class RawParseUtils {
}
/**
+ * Parse a Git style timezone string in [+-]hhmm format
+ *
+ * @param b
+ * buffer to scan.
+ * @param ptr
+ * position within buffer to start parsing digits at.
+ * @param ptrResult
+ * optional location to return the new ptr value through. If null
+ * the ptr value will be discarded.
+ * @return the ZoneOffset represention of the timezone offset string.
+ * Invalid offsets default to UTC.
+ */
+ private static ZoneId parseZoneOffset(final byte[] b, int ptr,
+ MutableInteger ptrResult) {
+ int hhmm = parseBase10(b, ptr, ptrResult);
+ try {
+ return ZoneOffset.ofHoursMinutes(hhmm / 100, hhmm % 100);
+ } catch (DateTimeException e) {
+ return UTC;
+ }
+ }
+
+ /**
* Locate the first position after a given character.
*
* @param b
@@ -1035,17 +1056,19 @@ public final class RawParseUtils {
// character if there is no trailing LF.
final int tzBegin = lastIndexOfTrim(raw, ' ',
nextLF(raw, emailE - 1) - 2) + 1;
- if (tzBegin <= emailE) // No time/zone, still valid
- return new PersonIdent(name, email, 0, 0);
+ if (tzBegin <= emailE) { // No time/zone, still valid
+ return new PersonIdent(name, email, EPOCH, UTC);
+ }
final int whenBegin = Math.max(emailE,
lastIndexOfTrim(raw, ' ', tzBegin - 1) + 1);
- if (whenBegin >= tzBegin - 1) // No time/zone, still valid
- return new PersonIdent(name, email, 0, 0);
+ if (whenBegin >= tzBegin - 1) { // No time/zone, still valid
+ return new PersonIdent(name, email, EPOCH, UTC);
+ }
- final long when = parseLongBase10(raw, whenBegin, null);
- final int tz = parseTimeZoneOffset(raw, tzBegin);
- return new PersonIdent(name, email, when * 1000L, tz);
+ long when = parseLongBase10(raw, whenBegin, null);
+ return new PersonIdent(name, email, Instant.ofEpochSecond(when),
+ parseZoneOffset(raw, tzBegin, null));
}
/**
@@ -1083,16 +1106,16 @@ public final class RawParseUtils {
name = decode(raw, nameB, stop);
final MutableInteger ptrout = new MutableInteger();
- long when;
- int tz;
+ Instant when;
+ ZoneId tz;
if (emailE < stop) {
- when = parseLongBase10(raw, emailE + 1, ptrout);
- tz = parseTimeZoneOffset(raw, ptrout.value);
+ when = Instant.ofEpochSecond(parseLongBase10(raw, emailE + 1, ptrout));
+ tz = parseZoneOffset(raw, ptrout.value, null);
} else {
- when = 0;
- tz = 0;
+ when = EPOCH;
+ tz = UTC;
}
- return new PersonIdent(name, email, when * 1000L, tz);
+ return new PersonIdent(name, email, when, tz);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java
index 5611b1e78e..b6b19e0e7b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java
@@ -10,6 +10,8 @@
package org.eclipse.jgit.util;
import java.text.MessageFormat;
+import java.time.Duration;
+import java.time.Instant;
import java.util.Date;
import org.eclipse.jgit.internal.JGitText;
@@ -42,12 +44,29 @@ public class RelativeDateFormatter {
* @return age of given {@link java.util.Date} compared to now formatted in
* the same relative format as returned by
* {@code git log --relative-date}
+ * @deprecated Use {@link #format(Instant)} instead.
*/
+ @Deprecated(since = "7.2")
@SuppressWarnings("boxing")
public static String format(Date when) {
+ return format(when.toInstant());
+ }
- long ageMillis = SystemReader.getInstance().getCurrentTime()
- - when.getTime();
+ /**
+ * Get age of given {@link java.time.Instant} compared to now formatted in the
+ * same relative format as returned by {@code git log --relative-date}
+ *
+ * @param when
+ * an instant to format
+ * @return age of given instant compared to now formatted in
+ * the same relative format as returned by
+ * {@code git log --relative-date}
+ * @since 7.2
+ */
+ @SuppressWarnings("boxing")
+ public static String format(Instant when) {
+ long ageMillis = Duration
+ .between(when, SystemReader.getInstance().now()).toMillis();
// shouldn't happen in a perfect world
if (ageMillis < 0)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java
index cf06172c17..e3e3e04fd9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java
@@ -13,8 +13,8 @@ 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.SignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.SignatureVerifier.TrustLevel;
import org.eclipse.jgit.lib.PersonIdent;
/**
@@ -39,29 +39,34 @@ public final class SignatureUtils {
* to use for dates
* @return a textual representation of the {@link SignatureVerification},
* using LF as line separator
+ *
+ * @since 7.0
*/
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');
+ if (verification.creationDate() != null) {
+ // Use the creator's timezone for the signature date
+ PersonIdent dateId = new PersonIdent(creator,
+ verification.creationDate().toInstant());
+ 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)));
+ verification.keyFingerprint().toUpperCase(Locale.ROOT)));
result.append('\n');
- if (!StringUtils.isEmptyOrNull(verification.getSigner())) {
+ if (!StringUtils.isEmptyOrNull(verification.signer())) {
result.append(
MessageFormat.format(JGitText.get().verifySignatureIssuer,
- verification.getSigner()));
+ verification.signer()));
result.append('\n');
}
String msg;
- if (verification.getVerified()) {
- if (verification.isExpired()) {
+ if (verification.verified()) {
+ if (verification.expired()) {
msg = JGitText.get().verifySignatureExpired;
} else {
msg = JGitText.get().verifySignatureGood;
@@ -69,14 +74,14 @@ public final class SignatureUtils {
} else {
msg = JGitText.get().verifySignatureBad;
}
- result.append(MessageFormat.format(msg, verification.getKeyUser()));
- if (!TrustLevel.UNKNOWN.equals(verification.getTrustLevel())) {
+ result.append(MessageFormat.format(msg, verification.keyUser()));
+ if (!TrustLevel.UNKNOWN.equals(verification.trustLevel())) {
result.append(' ' + MessageFormat
.format(JGitText.get().verifySignatureTrust, verification
- .getTrustLevel().name().toLowerCase(Locale.ROOT)));
+ .trustLevel().name().toLowerCase(Locale.ROOT)));
}
result.append('\n');
- msg = verification.getMessage();
+ msg = verification.message();
if (!StringUtils.isEmptyOrNull(msg)) {
result.append(msg);
result.append('\n');
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/Stats.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Stats.java
index d957deb34c..efa6e7ddc3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/Stats.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/Stats.java
@@ -43,14 +43,18 @@ public class Stats {
}
/**
- * @return number of the added values
+ * Returns the number of added values
+ *
+ * @return the number of added values
*/
public int count() {
return n;
}
/**
- * @return minimum of the added values
+ * Returns the smallest value added
+ *
+ * @return the smallest value added
*/
public double min() {
if (n < 1) {
@@ -60,7 +64,9 @@ public class Stats {
}
/**
- * @return maximum of the added values
+ * Returns the biggest value added
+ *
+ * @return the biggest value added
*/
public double max() {
if (n < 1) {
@@ -70,9 +76,10 @@ public class Stats {
}
/**
- * @return average of the added values
+ * Returns the average of the added values
+ *
+ * @return the average of the added values
*/
-
public double avg() {
if (n < 1) {
return Double.NaN;
@@ -81,7 +88,9 @@ public class Stats {
}
/**
- * @return variance of the added values
+ * Returns the variance of the added values
+ *
+ * @return the variance of the added values
*/
public double var() {
if (n < 2) {
@@ -91,7 +100,9 @@ public class Stats {
}
/**
- * @return standard deviation of the added values
+ * Returns the standard deviation of the added values
+ *
+ * @return the standard deviation of the added values
*/
public double stddev() {
return Math.sqrt(this.var());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
index 2fbd12dcc5..e381a3bcc9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
@@ -278,6 +278,44 @@ public final class StringUtils {
}
/**
+ * Remove the specified character from beginning and end of a string
+ * <p>
+ * If the character repeats, all copies
+ *
+ * @param str input string
+ * @param c character to remove
+ * @return the input string with c
+ * @since 7.2
+ */
+ public static String trim(String str, char c) {
+ if (str == null || str.length() == 0) {
+ return str;
+ }
+
+ int endPos = str.length()-1;
+ while (endPos >= 0 && str.charAt(endPos) == c) {
+ endPos--;
+ }
+
+ // Whole string is c
+ if (endPos == -1) {
+ return EMPTY;
+ }
+
+ int startPos = 0;
+ while (startPos < endPos && str.charAt(startPos) == c) {
+ startPos++;
+ }
+
+ if (startPos == 0 && endPos == str.length()-1) {
+ // No need to copy
+ return str;
+ }
+
+ return str.substring(startPos, endPos+1);
+ }
+
+ /**
* Appends {@link Constants#DOT_GIT_EXT} unless the given name already ends
* with that suffix.
*
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 ed62c71371..22b82b3610 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -23,10 +23,12 @@ import java.nio.charset.UnsupportedCharsetException;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicReference;
@@ -169,6 +171,11 @@ public abstract class SystemReader {
}
@Override
+ public Instant now() {
+ return Instant.now();
+ }
+
+ @Override
public int getTimezone(long when) {
return getTimeZone().getOffset(when) / (60 * 1000);
}
@@ -230,9 +237,19 @@ public abstract class SystemReader {
}
@Override
+ public Instant now() {
+ return delegate.now();
+ }
+
+ @Override
public int getTimezone(long when) {
return delegate.getTimezone(when);
}
+
+ @Override
+ public ZoneOffset getTimeZoneAt(Instant when) {
+ return delegate.getTimeZoneAt(when);
+ }
}
private static volatile SystemReader INSTANCE = DEFAULT;
@@ -503,10 +520,37 @@ public abstract class SystemReader {
* Get the current system time
*
* @return the current system time
+ *
+ * @deprecated Use {@link #now()}
*/
+ @Deprecated(since = "7.1")
public abstract long getCurrentTime();
/**
+ * Get the current system time
+ *
+ * @return the current system time
+ *
+ * @since 7.1
+ */
+ public Instant now() {
+ // Subclasses overriding getCurrentTime should keep working
+ // TODO(ifrade): Once we remove getCurrentTime, use Instant.now()
+ return Instant.ofEpochMilli(getCurrentTime());
+ }
+
+ /**
+ * Get "now" as civil time, in the System timezone
+ *
+ * @return the current system time
+ *
+ * @since 7.1
+ */
+ public LocalDateTime civilNow() {
+ return LocalDateTime.ofInstant(now(), getTimeZoneId());
+ }
+
+ /**
* Get clock instance preferred by this system.
*
* @return clock instance preferred by this system.
@@ -522,20 +566,48 @@ public abstract class SystemReader {
* @param when
* a system timestamp
* @return the local time zone
+ *
+ * @deprecated Use {@link #getTimeZoneAt(Instant)} instead.
*/
+ @Deprecated(since = "7.1")
public abstract int getTimezone(long when);
/**
+ * Get the local time zone offset at "when" time
+ *
+ * @param when
+ * a system timestamp
+ * @return the local time zone
+ * @since 7.1
+ */
+ public ZoneOffset getTimeZoneAt(Instant when) {
+ return getTimeZoneId().getRules().getOffset(when);
+ }
+
+ /**
* Get system time zone, possibly mocked for testing
*
* @return system time zone, possibly mocked for testing
* @since 1.2
+ *
+ * @deprecated Use {@link #getTimeZoneId()}
*/
+ @Deprecated(since = "7.1")
public TimeZone getTimeZone() {
return TimeZone.getDefault();
}
/**
+ * Get system time zone, possibly mocked for testing
+ *
+ * @return system time zone, possibly mocked for testing
+ * @since 7.1
+ */
+ public ZoneId getTimeZoneId() {
+ return ZoneId.systemDefault();
+ }
+
+ /**
* Get the locale to use
*
* @return the locale to use
@@ -670,9 +742,7 @@ public abstract class SystemReader {
}
private String getOsName() {
- return AccessController.doPrivileged(
- (PrivilegedAction<String>) () -> getProperty("os.name") //$NON-NLS-1$
- );
+ return getProperty("os.name"); //$NON-NLS-1$
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
index 2385865674..4b9706a3ce 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
@@ -147,44 +147,6 @@ public class AutoLFInputStream extends InputStream {
&& flags.contains(StreamFlag.FOR_CHECKOUT);
}
- /**
- * Creates a new InputStream, wrapping the specified stream.
- *
- * @param in
- * raw input stream
- * @param detectBinary
- * whether binaries should be detected
- * @since 2.0
- * @deprecated since 5.9, use {@link #create(InputStream, StreamFlag...)}
- * instead
- */
- @Deprecated
- public AutoLFInputStream(InputStream in, boolean detectBinary) {
- this(in, detectBinary, false);
- }
-
- /**
- * Creates a new InputStream, wrapping the specified stream.
- *
- * @param in
- * raw input stream
- * @param detectBinary
- * whether binaries should be detected
- * @param abortIfBinary
- * throw an IOException if the file is binary
- * @since 3.3
- * @deprecated since 5.9, use {@link #create(InputStream, StreamFlag...)}
- * instead
- */
- @Deprecated
- public AutoLFInputStream(InputStream in, boolean detectBinary,
- boolean abortIfBinary) {
- this.in = in;
- this.detectBinary = detectBinary;
- this.abortIfBinary = abortIfBinary;
- this.forCheckout = false;
- }
-
@Override
public int read() throws IOException {
final int read = read(single, 0, 1);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java
index 4764676c88..13982b133c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java
@@ -11,8 +11,6 @@ package org.eclipse.jgit.util.io;
import java.io.IOException;
import java.io.Writer;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import org.eclipse.jgit.util.SystemReader;
@@ -35,10 +33,7 @@ public class ThrowingPrintWriter extends Writer {
*/
public ThrowingPrintWriter(Writer out) {
this.out = out;
- LF = AccessController
- .doPrivileged((PrivilegedAction<String>) () -> SystemReader
- .getInstance().getProperty("line.separator") //$NON-NLS-1$
- );
+ LF = SystemReader.getInstance().getProperty("line.separator"); //$NON-NLS-1$
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java
index c3a1c4e3bb..7e950f6529 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java
@@ -14,7 +14,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Deque;
-import java.util.Iterator;
/**
* An InputStream which reads from one or more InputStreams.
@@ -164,14 +163,14 @@ public class UnionInputStream extends InputStream {
public void close() throws IOException {
IOException err = null;
- for (Iterator<InputStream> i = streams.iterator(); i.hasNext();) {
+ for (InputStream stream : streams) {
try {
- i.next().close();
+ stream.close();
} catch (IOException closeError) {
err = closeError;
}
- i.remove();
}
+ streams.clear();
if (err != null)
throw err;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/time/ProposedTimestamp.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/time/ProposedTimestamp.java
index a5ee1070d0..a20eaaf908 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/time/ProposedTimestamp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/time/ProposedTimestamp.java
@@ -138,7 +138,10 @@ public abstract class ProposedTimestamp implements AutoCloseable {
* Get time since epoch, with up to microsecond resolution.
*
* @return time since epoch, with up to microsecond resolution.
+ *
+ * @deprecated Use instant() instead
*/
+ @Deprecated(since = "7.2")
public Timestamp timestamp() {
return Timestamp.from(instant());
}
@@ -147,7 +150,10 @@ public abstract class ProposedTimestamp implements AutoCloseable {
* Get time since epoch, with up to millisecond resolution.
*
* @return time since epoch, with up to millisecond resolution.
+ *
+ * @deprecated Use instant() instead
*/
+ @Deprecated(since = "7.2")
public Date date() {
return new Date(millis());
}
diff --git a/pom.xml b/pom.xml
index 590dffa7c5..ecae118eb4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
<packaging>pom</packaging>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
<name>JGit - Parent</name>
<url>${jgit-url}</url>
@@ -33,8 +33,8 @@
</description>
<scm>
- <url>https://git.eclipse.org/r/plugins/gitiles/jgit/jgit</url>
- <connection>scm:git:https://git.eclipse.org/r/jgit/jgit</connection>
+ <url>https://eclipse.gerrithub.io/plugins/gitiles/eclipse-jgit/jgit</url>
+ <connection>scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit</connection>
</scm>
<ciManagement>
@@ -95,8 +95,8 @@
</mailingLists>
<issueManagement>
- <url>https://bugs.eclipse.org/bugs/buglist.cgi?query_format=advanced;component=JGit;product=JGit;classification=Technology</url>
- <system>Bugzilla</system>
+ <url>https://github.com/eclipse-jgit/jgit/issues</url>
+ <system>GitHub Issues</system>
</issueManagement>
<licenses>
@@ -118,37 +118,37 @@
<project.build.outputTimestamp>${commit.time.iso}</project.build.outputTimestamp>
- <jgit-last-release-version>6.9.0.202403050737-r</jgit-last-release-version>
- <ant-version>1.10.14</ant-version>
- <apache-sshd-version>2.12.0</apache-sshd-version>
+ <jgit-last-release-version>7.1.0.202411261347-r</jgit-last-release-version>
+ <ant-version>1.10.15</ant-version>
+ <apache-sshd-version>2.15.0</apache-sshd-version>
<jsch-version>0.1.55</jsch-version>
<jzlib-version>1.1.3</jzlib-version>
<javaewah-version>1.2.3</javaewah-version>
<junit-version>4.13.2</junit-version>
<test-fork-count>1C</test-fork-count>
- <args4j-version>2.33</args4j-version>
- <commons-compress-version>1.26.0</commons-compress-version>
+ <args4j-version>2.37</args4j-version>
+ <commons-compress-version>1.27.1</commons-compress-version>
<osgi-core-version>6.0.0</osgi-core-version>
- <servlet-api-version>6.0.0</servlet-api-version>
- <jetty-version>12.0.9</jetty-version>
- <japicmp-version>0.18.5</japicmp-version>
+ <servlet-api-version>6.1.0</servlet-api-version>
+ <jetty-version>12.0.16</jetty-version>
+ <japicmp-version>0.23.1</japicmp-version>
<httpclient-version>4.5.14</httpclient-version>
<httpcore-version>4.4.16</httpcore-version>
<slf4j-version>1.7.36</slf4j-version>
- <maven-javadoc-plugin-version>3.6.3</maven-javadoc-plugin-version>
- <gson-version>2.10.1</gson-version>
- <bouncycastle-version>1.77</bouncycastle-version>
- <spotbugs-maven-plugin-version>4.8.3.1</spotbugs-maven-plugin-version>
- <maven-project-info-reports-plugin-version>3.5.1</maven-project-info-reports-plugin-version>
- <maven-jxr-plugin-version>3.3.2</maven-jxr-plugin-version>
- <maven-surefire-plugin-version>3.2.5</maven-surefire-plugin-version>
+ <maven-javadoc-plugin-version>3.11.2</maven-javadoc-plugin-version>
+ <gson-version>2.12.1</gson-version>
+ <bouncycastle-version>1.80</bouncycastle-version>
+ <spotbugs-maven-plugin-version>4.9.1.0</spotbugs-maven-plugin-version>
+ <maven-project-info-reports-plugin-version>3.8.0</maven-project-info-reports-plugin-version>
+ <maven-jxr-plugin-version>3.6.0</maven-jxr-plugin-version>
+ <maven-surefire-plugin-version>3.5.2</maven-surefire-plugin-version>
<maven-surefire-report-plugin-version>${maven-surefire-plugin-version}</maven-surefire-report-plugin-version>
- <maven-compiler-plugin-version>3.12.1</maven-compiler-plugin-version>
+ <maven-compiler-plugin-version>3.14.0</maven-compiler-plugin-version>
<plexus-compiler-version>2.13.0</plexus-compiler-version>
<hamcrest-version>2.2</hamcrest-version>
- <assertj-version>3.25.3</assertj-version>
- <jna-version>5.14.0</jna-version>
- <byte-buddy-version>1.14.12</byte-buddy-version>
+ <assertj-version>3.27.3</assertj-version>
+ <jna-version>5.16.0</jna-version>
+ <byte-buddy-version>1.17.1</byte-buddy-version>
<!-- Properties to enable jacoco code coverage analysis -->
<sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
@@ -184,7 +184,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>3.3.0</version>
+ <version>3.4.2</version>
<configuration>
<archive>
<manifestEntries>
@@ -208,13 +208,13 @@
<plugin>
<artifactId>maven-clean-plugin</artifactId>
- <version>3.3.2</version>
+ <version>3.4.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
- <version>3.5.1</version>
+ <version>3.6.0</version>
</plugin>
<plugin>
@@ -226,13 +226,13 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
- <version>3.6.1</version>
+ <version>3.8.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
- <version>3.3.0</version>
+ <version>3.3.1</version>
</plugin>
<plugin>
@@ -255,7 +255,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
- <version>3.5.0</version>
+ <version>3.6.0</version>
</plugin>
<plugin>
@@ -277,7 +277,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
- <version>3.21.2</version>
+ <version>3.26.0</version>
<configuration>
<inputEncoding>${project.build.sourceEncoding}</inputEncoding>
<minimumTokens>100</minimumTokens>
@@ -300,17 +300,17 @@
<plugin>
<groupId>org.eclipse.cbi.maven.plugins</groupId>
<artifactId>eclipse-jarsigner-plugin</artifactId>
- <version>1.4.3</version>
+ <version>1.5.2</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.8.11</version>
+ <version>0.8.12</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
- <version>4.0.0-M13</version>
+ <version>4.0.0-M16</version>
<dependencies>
<dependency><!-- add support for ssh/scp -->
<groupId>org.apache.maven.wagon</groupId>
@@ -337,12 +337,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
- <version>3.1.1</version>
+ <version>3.1.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
- <version>3.1.1</version>
+ <version>3.1.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -357,7 +357,7 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
- <version>2.7.13</version>
+ <version>3.4.3</version>
</plugin>
<plugin>
<groupId>org.eclipse.dash</groupId>
@@ -367,12 +367,12 @@
<plugin>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
- <version>2.7.10</version>
+ <version>2.9.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-artifact-plugin</artifactId>
- <version>3.5.0</version>
+ <version>3.6.0</version>
<configuration>
<ignore>**/*cyclonedx.json</ignore>
<reproducible>true</reproducible>
@@ -381,7 +381,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
- <version>3.4.1</version>
+ <version>3.5.0</version>
</plugin>
</plugins>
</pluginManagement>
@@ -623,7 +623,7 @@
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
- <version>7.0.0</version>
+ <version>9.0.1</version>
<executions>
<execution>
<id>get-the-git-infos</id>
@@ -642,18 +642,18 @@
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
- <version>3.0.2</version>
+ <version>4.1.1</version>
<dependencies>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy</artifactId>
- <version>4.0.15</version>
+ <version>4.0.21</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-ant</artifactId>
- <version>4.0.15</version>
+ <version>4.0.21</version>
<scope>runtime</scope>
</dependency>
</dependencies>
@@ -884,15 +884,33 @@
</dependency>
<dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.18.0</version>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>${commons-compress-version}</version>
</dependency>
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.18.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.3.5</version>
+ </dependency>
+
+ <dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
- <version>1.9</version>
+ <version>1.10</version>
<optional>true</optional>
</dependency>
@@ -989,7 +1007,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
+ <version>5.15.2</version>
</dependency>
<dependency>
@@ -1098,7 +1116,7 @@
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
- <version>3.36.0</version>
+ <version>3.40.0</version>
</dependency>
</dependencies>
</plugin>
diff --git a/tools/BUILD b/tools/BUILD
index 8c424b357b..844f0049e6 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -10,6 +10,7 @@ default_java_toolchain(
java_runtime = "@rules_java//toolchains:remotejdk_17",
package_configuration = [
":error_prone",
+ ":error_prone_tests",
],
source_version = "17",
target_version = "17",
@@ -22,6 +23,7 @@ default_java_toolchain(
java_runtime = "@rules_java//toolchains:remotejdk_21",
package_configuration = [
":error_prone",
+ ":error_prone_tests",
],
source_version = "21",
target_version = "21",
@@ -32,9 +34,7 @@ default_java_toolchain(
# enabled. This warnings list is originally based on:
# https://github.com/bazelbuild/BUILD_file_generator/blob/master/tools/bazel_defs/java.bzl
# However, feel free to add any additional errors. Thus far they have all been pretty useful.
-java_package_configuration(
- name = "error_prone",
- javacopts = [
+errorprone_checks = [
"-XepDisableWarningsInGeneratedCode",
# The XepDisableWarningsInGeneratedCode disables only warnings, but
# not errors. We should manually exclude all files generated by
@@ -422,37 +422,57 @@ java_package_configuration(
"-Xep:WrongOneof:ERROR",
"-Xep:XorPower:ERROR",
"-Xep:ZoneIdOfZ:ERROR",
- ],
+]
+
+
+exclude_in_tests = ["-Xep:EmptyBlockTag:WARN",
+ "-Xep:MissingSummary:WARN"]
+
+java_package_configuration(
+ name = "error_prone",
+ javacopts = errorprone_checks,
packages = ["error_prone_packages"],
)
+java_package_configuration(
+ name = "error_prone_tests",
+ javacopts = [ check for check in errorprone_checks if check not in exclude_in_tests],
+ packages = ["error_prone_packages_test"],
+)
+
package_group(
name = "error_prone_packages",
packages = [
- "//org.eclipse.jgit.ant.test/...",
"//org.eclipse.jgit.ant/...",
"//org.eclipse.jgit.archive/...",
- "//org.eclipse.jgit.gpg.bc.test/...",
"//org.eclipse.jgit.gpg.bc/...",
"//org.eclipse.jgit.http.apache/...",
"//org.eclipse.jgit.http.server/...",
- "//org.eclipse.jgit.http.test/...",
"//org.eclipse.jgit.junit.ssh/...",
"//org.eclipse.jgit.junit/...",
"//org.eclipse.jgit.junit/http/...",
- "//org.eclipse.jgit.lfs.server.test/...",
"//org.eclipse.jgit.lfs.server/...",
- "//org.eclipse.jgit.lfs.test/...",
"//org.eclipse.jgit.lfs/...",
- "//org.eclipse.jgit.pgm.test/...",
"//org.eclipse.jgit.pgm/...",
"//org.eclipse.jgit.ssh.apache.agent/...",
- "//org.eclipse.jgit.ssh.apache.test/...",
"//org.eclipse.jgit.ssh.apache/...",
- "//org.eclipse.jgit.ssh.jsch.test/...",
"//org.eclipse.jgit.ssh.jsch/...",
- "//org.eclipse.jgit.test/...",
"//org.eclipse.jgit.ui/...",
"//org.eclipse.jgit/...",
],
)
+
+package_group(
+ name = "error_prone_packages_test",
+ packages = [
+ "//org.eclipse.jgit.ant.test/...",
+ "//org.eclipse.jgit.gpg.bc.test/...",
+ "//org.eclipse.jgit.http.test/...",
+ "//org.eclipse.jgit.lfs.server.test/...",
+ "//org.eclipse.jgit.lfs.test/...",
+ "//org.eclipse.jgit.pgm.test/...",
+ "//org.eclipse.jgit.ssh.apache.test/...",
+ "//org.eclipse.jgit.ssh.jsch.test/...",
+ "//org.eclipse.jgit.test/...",
+ ],
+)
diff --git a/tools/workspace_status.py b/tools/workspace_status.py
index ca9e0a98c9..1186a4a77a 100644
--- a/tools/workspace_status.py
+++ b/tools/workspace_status.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2020, David Ostrovsky <david@ostrovsky.org> and others
#
# This program and the accompanying materials are made available under the