Merge branch 'stable-7.0' into stable-7.1 * stable-7.0: Do not always refresh packed-refs during ref updates Change-Id: I6f249ceb085105ae3309c6d1312ffa9482569fb6
diff --git a/Documentation/config-options.md b/Documentation/config-options.md index b30b958..2904824 100644 --- a/Documentation/config-options.md +++ b/Documentation/config-options.md
@@ -31,6 +31,7 @@ | `core.dfs.blockSize` | `64 kiB` | ⃞ | Size in bytes of a single window read in from the pack file into the DFS block cache. | | `core.dfs.concurrencyLevel` | `32` | ⃞ | The estimated number of threads concurrently accessing the DFS block cache. | | `core.dfs.deltaBaseCacheLimit` | `10 MiB` | ⃞ | Maximum number of bytes to hold in per-reader DFS delta base cache. | +| `core.dfs.loadRevIndexInParallel` | false; | ⃞ | Try to load the reverse index in parallel with the bitmap index. | | `core.dfs.streamFileThreshold` | `50 MiB` | ⃞ | The size threshold beyond which objects must be streamed. | | `core.dfs.streamBuffer` | Block size of the pack | ⃞ | 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` | ⃞ | Ratio of DFS block cache to occupy with a copied pack. Values between `0` and `1.0`. |
diff --git a/WORKSPACE b/WORKSPACE index 34caf9d..e1adb8a 100644 --- a/WORKSPACE +++ b/WORKSPACE
@@ -122,18 +122,18 @@ sha1 = "c070ac920e72023ae9ab0a3f3a866bece284b470", ) -JNA_VERS = "5.14.0" +JNA_VERS = "5.15.0" maven_jar( name = "jna", artifact = "net.java.dev.jna:jna:" + JNA_VERS, - sha1 = "67bf3eaea4f0718cb376a181a629e5f88fa1c9dd", + sha1 = "01ee1d80ff44f08280188f7c0e740d57207841ac", ) maven_jar( name = "jna-platform", artifact = "net.java.dev.jna:jna-platform:" + JNA_VERS, - sha1 = "28934d48aed814f11e4c584da55c49fa7032b31b", + sha1 = "86b502cad57d45da172b5e3231c537b042e296ef", ) maven_jar( @@ -174,14 +174,14 @@ maven_jar( name = "commons-lang3", - artifact = "org.apache.commons:commons-lang3:3.16.0", - sha1 = "3eb54effe40946dfb06dc5cd6c7ce4116cd51ea4", + artifact = "org.apache.commons:commons-lang3:3.17.0", + sha1 = "b17d2136f0460dcc0d2016ceefca8723bdf4ee70", ) maven_jar( name = "commons-io", - artifact = "commons-io:commons-io:2.16.1", - sha1 = "377d592e740dc77124e0901291dbfaa6810a200e", + artifact = "commons-io:commons-io:2.17.0", + sha1 = "ddcc8433eb019fb48fe25207c0278143f3e1d7e2", ) maven_jar( @@ -210,8 +210,8 @@ maven_jar( name = "mockito", - artifact = "org.mockito:mockito-core:5.12.0", - sha1 = "22f8bbaf478e6789164787fa411a3b5ed986e110", + artifact = "org.mockito:mockito-core:5.14.2", + sha1 = "f7bf936008d7664e2002c3faf0c02071c8d10e7c", ) maven_jar( @@ -220,18 +220,18 @@ sha1 = "0d26263eb7524252d98e602fc6942996a3195e29", ) -BYTE_BUDDY_VERSION = "1.15.0" +BYTE_BUDDY_VERSION = "1.15.10" maven_jar( name = "bytebuddy", artifact = "net.bytebuddy:byte-buddy:" + BYTE_BUDDY_VERSION, - sha1 = "a5b1159b91c5334015de0f22ab4b1188cd42bbff", + sha1 = "635c873fadd853c084f84fdc3cbd58c5dd8537f9", ) maven_jar( name = "bytebuddy-agent", artifact = "net.bytebuddy:byte-buddy-agent:" + BYTE_BUDDY_VERSION, - sha1 = "e32740c43acebaac9d55b86399ecf6a5df3c17fb", + sha1 = "0e8eb255b2c378b9a6c7341e7b0e12f0a5636377", ) maven_jar( @@ -246,90 +246,82 @@ sha1 = "527175ca6d81050b53bdd4c457a6d6e017626b0e", ) -JETTY_VER = "12.0.12" +JETTY_VER = "12.0.15" maven_jar( name = "jetty-servlet", artifact = "org.eclipse.jetty.ee10:jetty-ee10-servlet:" + JETTY_VER, - sha1 = "12f25f260a8f9fb519b6d3058260564277e618cd", - src_sha1 = "16a22f6ed585c6dcab07d111de290301373db1c7", + sha1 = "a9362717fa1756f9f1f18e0ef2ce671e742b7afb", ) maven_jar( name = "jetty-security", artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VER, - sha1 = "962d2c5748f750aae667399481915bd02a9746e0", - src_sha1 = "e953d0e19e6e420f5e7c1f62424c4bbe0d385d15", + sha1 = "e15efd84ed53277f8e893574edaed4734c161f44", ) maven_jar( name = "jetty-server", artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VER, - sha1 = "77591ab10113d7de7eb8a01bb4d9d23b234aebd1", - src_sha1 = "952873398a59bc5a964d9d2d130227af807559e9", + sha1 = "2bd3742c6831e42c6ebfa9c990386a8dca71dee8", ) maven_jar( name = "jetty-session", artifact = "org.eclipse.jetty:jetty-session:" + JETTY_VER, - sha1 = "fceae460fb4677a6cec80d3b70b1fc3ae2a114a7", - src_sha1 = "ba95d5e17a3d7f09ca908d4c0773e913f3612425", + sha1 = "2819021282ff2f7fbaa53feb2fe063130bd4613c", ) maven_jar( name = "jetty-http", artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VER, - sha1 = "549fe58ae50b9c061d09803fb0d2659a93ce0ecd", - src_sha1 = "c7afad980b9eade12b473172a9d069d96d016677", + sha1 = "a36fcfde8316b374102c5b43d7247ec501e906d8", ) maven_jar( name = "jetty-io", artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VER, - sha1 = "4e2cd5c23e8ba550238f35c361c22ffb1e7bf00c", - src_sha1 = "42397797c496a51520900a529ba7e1b315aba768", + sha1 = "e1657f842a0e362171a8a41f35d85cdba1a47872", ) maven_jar( name = "jetty-util", artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VER, - sha1 = "f7b1e4f835c38d12668b426d327118e767d2f1d4", - src_sha1 = "0fdb95a123cc50f6adf4154edc5316cabca05f58", + sha1 = "9fbd3ac58607af034ad065b366798798bb5f7b5e", ) maven_jar( name = "jetty-util-ajax", artifact = "org.eclipse.jetty:jetty-util-ajax:" + JETTY_VER, - sha1 = "330e692032c82c305cd52c6a8e9bfba0f1da2555", - src_sha1 = "d6b9274b3ef0419a6ba869c828fd0c59c570cd9d", + sha1 = "d5c2f0ea177c5c178874101c186cc9729cf7ea92", ) -BOUNCYCASTLE_VER = "1.78.1" +BOUNCYCASTLE_VER = "1.79" maven_jar( name = "bcpg", artifact = "org.bouncycastle:bcpg-jdk18on:" + BOUNCYCASTLE_VER, - sha1 = "6c8dbcec20355278ec54840e735f63db2479150e", - src_sha1 = "2ddef60d84dd8c14ebce4c13100f0bc55fed6922", + sha1 = "904dd8a8e1c9f7d58d1ffa7f4ca3fb00736a601f", + src_sha1 = "9e372826141edb213d5921131ee68dc276dc99ef", ) maven_jar( name = "bcprov", artifact = "org.bouncycastle:bcprov-jdk18on:" + BOUNCYCASTLE_VER, - sha1 = "39e9e45359e20998eb79c1828751f94a818d25f8", - src_sha1 = "70f58ec93da543dda6a21614b768cb2e386fd512", + sha1 = "4d8e2732bcee15f1db93df266c3f5b70ce5cac21", + src_sha1 = "8647816d667ee526a8e3a456229ac5f9f96d2315", ) maven_jar( name = "bcutil", artifact = "org.bouncycastle:bcutil-jdk18on:" + BOUNCYCASTLE_VER, - sha1 = "5353ca39fe2f148dab9ca1d637a43d0750456254", - src_sha1 = "8d2e0747f5d806f39a602f7f91610444d88c4e2c", + sha1 = "ecfc5aef97cc7676ea0de5c53c407b9f533f0ad5", + src_sha1 = "00df03977fb0b80395da655623abca9d7d7dcb66", ) maven_jar( name = "bcpkix", artifact = "org.bouncycastle:bcpkix-jdk18on:" + BOUNCYCASTLE_VER, - sha1 = "17b3541f736df97465f87d9f5b5dfa4991b37bb3", - src_sha1 = "3aeaf221772ad0c9c04593688cb86c6eb74d48b9", + sha1 = "7693cec3b8779b74b35466dcaeeaac7409872954", + src_sha1 = "57a60d1d9f75320eef70a095dfae679d97ade1c2", )
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF index 251a2f7..3644186 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 @@ Automatic-Module-Name: org.eclipse.jgit.ant.test Bundle-SymbolicName: org.eclipse.jgit.ant.test Bundle-Vendor: %Bundle-Vendor -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-17 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.ant.tasks;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", + org.eclipse.jgit.ant.tasks;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.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 e32e54f..a145760 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.2-SNAPSHOT</version> + <version>7.1.2-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 b59b2be..b22bdb9 100644 --- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -3,13 +3,13 @@ Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.ant Bundle-SymbolicName: org.eclipse.jgit.ant -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-17 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.storage.file;version="[7.0.2,7.1.0)" + org.eclipse.jgit.storage.file;version="[7.1.2,7.2.0)" Bundle-Localization: OSGI-INF/l10n/plugin Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.jgit.ant;version="7.0.2", - org.eclipse.jgit.ant.tasks;version="7.0.2"; +Export-Package: org.eclipse.jgit.ant;version="7.1.2", + org.eclipse.jgit.ant.tasks;version="7.1.2"; 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 2a78006..9fb41bb 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-Name: org.eclipse.jgit.ant - Sources Bundle-SymbolicName: org.eclipse.jgit.ant.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ant;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ant;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml index b60743d..0376cef 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ant</artifactId>
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF index 1318c44..c9d4352 100644 --- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.archive Bundle-SymbolicName: org.eclipse.jgit.archive -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: OSGI-INF/l10n/plugin Bundle-RequiredExecutionEnvironment: JavaSE-17 @@ -13,18 +13,18 @@ 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.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.nls;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", + org.eclipse.jgit.api;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.nls;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.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.2"; +Export-Package: org.eclipse.jgit.archive;version="7.1.2"; uses:="org.apache.commons.compress.archivers, org.osgi.framework, org.eclipse.jgit.api, org.eclipse.jgit.lib", - org.eclipse.jgit.archive.internal;version="7.0.2";x-internal:=true + org.eclipse.jgit.archive.internal;version="7.1.2";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 95915c2..9d11d98 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-Name: org.eclipse.jgit.archive - Sources Bundle-SymbolicName: org.eclipse.jgit.archive.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.archive;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.archive;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml index aa1e770..f557b01 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.archive</artifactId>
diff --git a/org.eclipse.jgit.benchmarks/pom.xml b/org.eclipse.jgit.benchmarks/pom.xml index dbf35a3..60f83cb 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.benchmarks</artifactId>
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 0000000..19297eb --- /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 499c0de..da71786 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.2-SNAPSHOT</version> + <version>7.1.2-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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ant</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.archive</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.http.apache</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.http.server</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.lfs</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.lfs.server</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.pgm</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ui</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ssh.apache</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.test</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ant.test</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.http.test</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.pgm.test</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.lfs.test</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.lfs.server.test</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId> - <version>7.0.2-SNAPSHOT</version> + <version>7.1.2-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 707c55b..c934a4f 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-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.gpg.bc.test Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.test -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.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.2,7.1.0)", - org.eclipse.jgit.gpg.bc.internal.keys;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util.sha1;version="[7.0.2,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.1.2,7.2.0)", + org.eclipse.jgit.gpg.bc.internal.keys;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util.sha1;version="[7.1.2,7.2.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 2310b6f..d821f19 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.2-SNAPSHOT</version> + <version>7.1.2-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 fed0610..d486c97 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.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 static void ensureBC() { } } - 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 String toString() { @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 @@ private static PGPPublicKey readAsc(InputStream in) @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 void testKeyRead() throws Exception { : 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 6bed05d..77fd134 100644 --- a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
@@ -3,31 +3,28 @@ 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.2,7.1.0)" +Fragment-Host: org.eclipse.jgit;bundle-version="[7.1.2,7.2.0)" Bundle-Vendor: %Bundle-Vendor Bundle-Localization: OSGI-INF/l10n/gpg_bc -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.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)", +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.internal;version="7.0.2";x-friends:="org.eclipse.jgit.gpg.bc.test", - org.eclipse.jgit.gpg.bc.internal.keys;version="7.0.2";x-friends:="org.eclipse.jgit.gpg.bc.test" +Export-Package: org.eclipse.jgit.gpg.bc.internal;version="7.1.2";x-friends:="org.eclipse.jgit.gpg.bc.test", + org.eclipse.jgit.gpg.bc.internal.keys;version="7.1.2";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 9dc9a9f..e8d547c 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-Name: org.eclipse.jgit.gpg.bc - Sources Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.gpg.bc/about.html b/org.eclipse.jgit.gpg.bc/about.html index fc527d5..92b9409 100644 --- a/org.eclipse.jgit.gpg.bc/about.html +++ b/org.eclipse.jgit.gpg.bc/about.html
@@ -58,32 +58,6 @@ 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 f51b978..e4c535d 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.gpg.bc</artifactId>
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 77ca2cd..9e7f98c 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 @@ 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/internal/BCText.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java index 705e195..fcae7c2 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 static BCText get() { // @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 static BCText get() { /***/ 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/BouncyCastleGpgSigner.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java index 1d187a5..adac9b1 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
@@ -105,7 +105,8 @@ public GpgSignature sign(Repository repository, GpgConfig config, PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator( new JcaPGPContentSignerBuilder( publicKey.getAlgorithm(), - HashAlgorithmTags.SHA256)); + HashAlgorithmTags.SHA256), + publicKey); signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, privateKey); PGPSignatureSubpacketGenerator subpackets = new PGPSignatureSubpacketGenerator(); subpackets.setIssuerFingerprint(false, publicKey);
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 3924d68..0000000 --- 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 fd030ee..0000000 --- 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 220aa28..0000000 --- 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 a659d38..a56e418 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 interface PassphraseSupplier { 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 static PGPSecretKey readSecretKey(InputStream in, 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))); + OpenedPGPKeyData data; + try (InputStream keyIn = new BufferedInputStream(in)) { + data = PGPSecretKeyParser.parse(keyIn, MAX_SEXPR_NESTING); } - 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); + PBEProtectionRemoverFactory decryptor = null; + if (isProtected(data)) { + decryptor = new JcePBEProtectionRemoverFactory( + passphraseSupplier.getPassphrase(), calculatorProvider); } - } - - /** - * 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; + 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); } - 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))); - } + 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())); } - return out.toByteArray(); - } - } - - /** - * GPG-style string de-quoting, which is basically C-style, with some - * literal CR/LF escaping. - * - * @param in - * buffer containing the quoted string - * @param from - * index after the opening quote in {@code in} - * @param to - * index of the closing quote in {@code in} - * @return the dequoted raw string value - * @throws StreamCorruptedException - * 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))); - } - } - return Arrays.copyOf(out, j); - } - - /** - * Extracts the key from a GPG name-value-pair key file. - * <p> - * Package-visible for tests only. - * </p> - * - * @param in - * {@link InputStream} to read from; should be buffered - * @return the raw key data as extracted from the file - * @throws IOException - * if the {@code in} stream cannot be read or does not contain a - * key - */ - static byte[] keyFromNameValueFormat(InputStream in) throws IOException { - // It would be nice if we could use RawParseUtils here, but GPG compares - // names case-insensitively. We're only interested in the "Key:" - // name-value pair. - int[] nameLow = { 'k', 'e', 'y', ':' }; - int[] nameCap = { 'K', 'E', 'Y', ':' }; - int nameIdx = 0; - for (;;) { - int next = in.read(); - if (next < 0) { - throw new EOFException(); - } - if (next == '\n') { - nameIdx = 0; - } else if (nameIdx >= 0) { - if (nameLow[nameIdx] == next || nameCap[nameIdx] == next) { - nameIdx++; - if (nameIdx == nameLow.length) { - break; - } - } else { - nameIdx = -1; - } - } - } - // We're after "Key:". Read the value as continuation lines. - int last = ':'; - byte[] rawData; - try (ByteArrayOutputStream out = new ByteArrayOutputStream(8192)) { - for (;;) { - int next = in.read(); - if (next < 0) { - break; - } - if (last == '\n') { - if (next == ' ' || next == '\t') { - // Continuation line; skip this whitespace - last = next; - continue; - } - break; // Not a continuation line - } - out.write(next); - last = next; - } - rawData = out.toByteArray(); - } - // GPG trims off trailing whitespace, and a line having only whitespace - // is a single LF. - try (ByteArrayOutputStream out = new ByteArrayOutputStream( - rawData.length)) { - int lineStart = 0; - boolean trimLeading = true; - while (lineStart < rawData.length) { - int nextLineStart = RawParseUtils.nextLF(rawData, lineStart); - if (trimLeading) { - while (lineStart < nextLineStart - && isGpgSpace(rawData[lineStart])) { - lineStart++; - } - } - // Trim trailing - int i = nextLineStart - 1; - while (lineStart < i && isGpgSpace(rawData[i])) { - i--; - } - if (i <= lineStart) { - // Empty line signifies LF - out.write('\n'); - trimLeading = true; - } else { - out.write(rawData, lineStart, i - lineStart + 1); - trimLeading = false; - } - lineStart = nextLineStart; - } - return out.toByteArray(); - } - } - - private static boolean isGpgSpace(int ch) { - return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; - } - - private static boolean isTokenChar(int ch) { - switch (ch) { - case '-': - case '.': - case '/': - case '_': - case ':': - case '*': - case '+': - case '=': - return true; + 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 18238b5..20d0e4d 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-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.http.apache Bundle-SymbolicName: org.eclipse.jgit.http.apache -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-Localization: OSGI-INF/l10n/plugin Bundle-Vendor: %Bundle-Vendor @@ -26,11 +26,11 @@ 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.2,7.1.0)", - org.eclipse.jgit.nls;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.http;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)" -Export-Package: org.eclipse.jgit.transport.http.apache;version="7.0.2"; + org.eclipse.jgit.annotations;version="[7.1.2,7.2.0)", + org.eclipse.jgit.nls;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.http;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.0)" +Export-Package: org.eclipse.jgit.transport.http.apache;version="7.1.2"; 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 89b7db1..04bbe37 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-Name: org.eclipse.jgit.http.apache - Sources Bundle-SymbolicName: org.eclipse.jgit.http.apache.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml index ec29cbd..11d92fc 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.apache</artifactId>
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF index b0b1ae1..2a262d1 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-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.http.server Bundle-SymbolicName: org.eclipse.jgit.http.server -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Localization: OSGI-INF/l10n/plugin Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.jgit.http.server;version="7.0.2", - org.eclipse.jgit.http.server.glue;version="7.0.2"; +Export-Package: org.eclipse.jgit.http.server;version="7.1.2", + org.eclipse.jgit.http.server.glue;version="7.1.2"; uses:="jakarta.servlet, jakarta.servlet.http", - org.eclipse.jgit.http.server.resolver;version="7.0.2"; + org.eclipse.jgit.http.server.resolver;version="7.1.2"; uses:="jakarta.servlet.http org.eclipse.jgit.transport.resolver, org.eclipse.jgit.lib, @@ -19,14 +19,14 @@ 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.2,7.1.0)", - org.eclipse.jgit.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.dfs;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.transport.parser;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.nls;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.resolver;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)" + org.eclipse.jgit.annotations;version="[7.1.2,7.2.0)", + org.eclipse.jgit.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.dfs;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.transport.parser;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.nls;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.resolver;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.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 4239578..0d9322c 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-Name: org.eclipse.jgit.http.server - Sources Bundle-SymbolicName: org.eclipse.jgit.http.server.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml index e13c39e..ecf80a3 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.server</artifactId>
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF index b338ec4..ec2d986 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-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.http.test Bundle-SymbolicName: org.eclipse.jgit.http.test -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-17 @@ -29,26 +29,26 @@ 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.2,7.1.0)", - org.eclipse.jgit.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.http.server;version="[7.0.2,7.1.0)", - org.eclipse.jgit.http.server.glue;version="[7.0.2,7.1.0)", - org.eclipse.jgit.http.server.resolver;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.dfs;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.reftable;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit.http;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.nls;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.http;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.http.apache;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.resolver;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", + org.eclipse.jgit.api;version="[7.1.2,7.2.0)", + org.eclipse.jgit.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.http.server;version="[7.1.2,7.2.0)", + org.eclipse.jgit.http.server.glue;version="[7.1.2,7.2.0)", + org.eclipse.jgit.http.server.resolver;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.dfs;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.reftable;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit.http;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.nls;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.http;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.http.apache;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.resolver;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.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 c69fe90..13c9acb 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF index e4c2174..d60dbed 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-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.junit.http Bundle-SymbolicName: org.eclipse.jgit.junit.http -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Localization: OSGI-INF/l10n/plugin Bundle-Vendor: %Bundle-Vendor Bundle-ActivationPolicy: lazy @@ -22,17 +22,17 @@ 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.2,7.1.0)", - org.eclipse.jgit.http.server;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.resolver;version="[7.0.2,7.1.0)", + org.eclipse.jgit.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.http.server;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.resolver;version="[7.1.2,7.2.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.2"; +Export-Package: org.eclipse.jgit.junit.http;version="7.1.2"; 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 b290a02..32c5433 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-Name: org.eclipse.jgit.junit.http - Sources Bundle-SymbolicName: org.eclipse.jgit.junit.http.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml index f853464..0ff0cd9 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.2-SNAPSHOT</version> + <version>7.1.2-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 dfa1b7a..1f9dcf4 100644 --- a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.junit.ssh Bundle-SymbolicName: org.eclipse.jgit.junit.ssh -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Localization: OSGI-INF/l10n/plugin Bundle-Vendor: %Bundle-Vendor Bundle-ActivationPolicy: lazy @@ -33,16 +33,16 @@ org.apache.sshd.server.subsystem;version="[2.14.0,2.15.0)", org.apache.sshd.sftp;version="[2.14.0,2.15.0)", org.apache.sshd.sftp.server;version="[2.14.0,2.15.0)", - org.eclipse.jgit.annotations;version="[7.0.2,7.1.0)", - org.eclipse.jgit.api;version="[7.0.2,7.1.0)", - org.eclipse.jgit.api.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", + org.eclipse.jgit.annotations;version="[7.1.2,7.2.0)", + org.eclipse.jgit.api;version="[7.1.2,7.2.0)", + org.eclipse.jgit.api.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.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.2" +Export-Package: org.eclipse.jgit.junit.ssh;version="7.1.2"
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 5f840d1..2fb060d 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-Name: org.eclipse.jgit.junit.ssh - Sources Bundle-SymbolicName: org.eclipse.jgit.junit.ssh.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml index 067374b..6595b5b 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.junit.ssh</artifactId>
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF index c7ae7ce..396df4c 100644 --- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -3,36 +3,36 @@ Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.junit Bundle-SymbolicName: org.eclipse.jgit.junit -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.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.2,7.1.0)", - org.eclipse.jgit.api;version="[7.0.2,7.1.0)", - org.eclipse.jgit.api.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.dircache;version="[7.0.2,7.1.0)", - org.eclipse.jgit.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.pack;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.util;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.merge;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="7.0.2", - org.eclipse.jgit.treewalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.treewalk.filter;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util.io;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util.time;version="[7.0.2,7.1.0)", +Import-Package: org.eclipse.jgit.annotations;version="[7.1.2,7.2.0)", + org.eclipse.jgit.api;version="[7.1.2,7.2.0)", + org.eclipse.jgit.api.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.dircache;version="[7.1.2,7.2.0)", + org.eclipse.jgit.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.pack;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.util;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.merge;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="7.1.2", + org.eclipse.jgit.treewalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.treewalk.filter;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util.io;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util.time;version="[7.1.2,7.2.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.2"; +Export-Package: org.eclipse.jgit.junit;version="7.1.2"; uses:="org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, @@ -45,4 +45,4 @@ org.junit.runners.model, org.junit.runner, org.eclipse.jgit.util.time", - org.eclipse.jgit.junit.time;version="7.0.2";uses:="org.eclipse.jgit.util.time" + org.eclipse.jgit.junit.time;version="7.1.2";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 ef76a33..8724466 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-Name: org.eclipse.jgit.junit - Sources Bundle-SymbolicName: org.eclipse.jgit.junit.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.junit;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.junit;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml index b63973f..aab23c8 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.junit</artifactId>
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 419fdb1..38f0d0b 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.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 TimeZone getTimeZone() { } @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 c8c56b2..2a482df 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 @@ private static Class<?> loadNewClass(Class<?> klass) 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.lfs.server.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF index 3ce8c3e..53f87cb 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-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.lfs.server.test Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-17 @@ -26,24 +26,24 @@ 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.2,7.1.0)", - org.eclipse.jgit.api.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit.http;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.server;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.server.fs;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.test;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.treewalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.treewalk.filter;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", + org.eclipse.jgit.api;version="[7.1.2,7.2.0)", + org.eclipse.jgit.api.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit.http;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.server;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.server.fs;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.test;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.treewalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.treewalk.filter;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.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 a3a99b5..1568471 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.2-SNAPSHOT</version> + <version>7.1.2-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 70d6029..925ada7 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-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.lfs.server Bundle-SymbolicName: org.eclipse.jgit.lfs.server -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Localization: OSGI-INF/l10n/plugin Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.jgit.lfs.server;version="7.0.2"; +Export-Package: org.eclipse.jgit.lfs.server;version="7.1.2"; uses:="jakarta.servlet.http, org.eclipse.jgit.lfs.lib", - org.eclipse.jgit.lfs.server.fs;version="7.0.2"; + org.eclipse.jgit.lfs.server.fs;version="7.1.2"; 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.2";x-internal:=true, - org.eclipse.jgit.lfs.server.s3;version="7.0.2"; + org.eclipse.jgit.lfs.server.internal;version="7.1.2";x-internal:=true, + org.eclipse.jgit.lfs.server.s3;version="7.1.2"; uses:="org.eclipse.jgit.lfs.server, org.eclipse.jgit.lfs.lib" Bundle-RequiredExecutionEnvironment: JavaSE-17 @@ -24,15 +24,15 @@ 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.2,7.1.0)", - org.eclipse.jgit.internal;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.internal;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.nls;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.http;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.http.apache;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", + org.eclipse.jgit.annotations;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.internal;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.nls;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.http;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.http.apache;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.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 2c07938..7f52aa9 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-Name: org.eclipse.jgit.lfs.server - Sources Bundle-SymbolicName: org.eclipse.jgit.lfs.server.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml index 679480b..819450b 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.2-SNAPSHOT</version> + <version>7.1.2-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 2d1acdb..38bbaa2 100644 --- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -3,28 +3,28 @@ Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.lfs.test Bundle-SymbolicName: org.eclipse.jgit.lfs.test -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-17 -Import-Package: org.eclipse.jgit.api;version="[7.0.2,7.1.0)", - org.eclipse.jgit.attributes;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.dfs;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.internal;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.http;version="[7.0.2,7.1.0)", - org.eclipse.jgit.treewalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.treewalk.filter;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", +Import-Package: org.eclipse.jgit.api;version="[7.1.2,7.2.0)", + org.eclipse.jgit.attributes;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.dfs;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.internal;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.http;version="[7.1.2,7.2.0)", + org.eclipse.jgit.treewalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.treewalk.filter;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.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.2";x-friends:="org.eclipse.jgit.lfs.server.test" +Export-Package: org.eclipse.jgit.lfs.test;version="7.1.2";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 79601ff..813823e 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs.test</artifactId>
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF index bb87bab..ec18f81 100644 --- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -3,32 +3,32 @@ Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.lfs Bundle-SymbolicName: org.eclipse.jgit.lfs -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Localization: OSGI-INF/l10n/plugin Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.jgit.lfs;version="7.0.2", - org.eclipse.jgit.lfs.errors;version="7.0.2", - org.eclipse.jgit.lfs.internal;version="7.0.2";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.2" +Export-Package: org.eclipse.jgit.lfs;version="7.1.2", + org.eclipse.jgit.lfs.errors;version="7.1.2", + org.eclipse.jgit.lfs.internal;version="7.1.2";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.1.2" 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.2,7.1.0)";resolution:=optional, - org.eclipse.jgit.api.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.attributes;version="[7.0.2,7.1.0)", - org.eclipse.jgit.diff;version="[7.0.2,7.1.0)", - org.eclipse.jgit.dircache;version="[7.0.2,7.1.0)", - org.eclipse.jgit.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.hooks;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.nls;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.storage.pack;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.http;version="[7.0.2,7.1.0)", - org.eclipse.jgit.treewalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.treewalk.filter;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util.io;version="[7.0.2,7.1.0)" + org.eclipse.jgit.annotations;version="[7.1.2,7.2.0)";resolution:=optional, + org.eclipse.jgit.api.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.attributes;version="[7.1.2,7.2.0)", + org.eclipse.jgit.diff;version="[7.1.2,7.2.0)", + org.eclipse.jgit.dircache;version="[7.1.2,7.2.0)", + org.eclipse.jgit.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.hooks;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.nls;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.storage.pack;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.http;version="[7.1.2,7.2.0)", + org.eclipse.jgit.treewalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.treewalk.filter;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util.io;version="[7.1.2,7.2.0)"
diff --git a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF index a1dd5b8..fa88770 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-Name: org.eclipse.jgit.lfs - Sources Bundle-SymbolicName: org.eclipse.jgit.lfs.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml index 80f047a..c435419 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs</artifactId>
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 98a3615..26a207f 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.2.qualifier" + version="7.1.2.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 bf22c74..6ea0915 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.2-SNAPSHOT</version> + <version>7.1.2-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 987a925..47cc345 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.2.qualifier" + version="7.1.2.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.2" match="equivalent"/> + <import plugin="org.eclipse.jgit" version="7.1.2" 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 7d2f4d9..87ec0ed 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.2-SNAPSHOT</version> + <version>7.1.2-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 459e77c..3246780 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.2.qualifier" + version="7.1.2.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.2" match="equivalent"/> + <import plugin="org.eclipse.jgit" version="7.1.2" 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 7c5b580..1fc7cbf 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.2-SNAPSHOT</version> + <version>7.1.2-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 fe4d8d7..8a83e3c 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.2.qualifier" + version="7.1.2.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.2" match="equivalent"/> + <import plugin="org.eclipse.jgit" version="7.1.2" 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 03c53de..3eeb973 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.2-SNAPSHOT</version> + <version>7.1.2-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 e991f99..5eba1c5 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.2.qualifier" + version="7.1.2.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.2" match="equivalent"/> + <import feature="org.eclipse.jgit" version="7.1.2" 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 57c62b2..3e90454 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.2-SNAPSHOT</version> + <version>7.1.2-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 aa6ad24..5f0691e 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.2.qualifier" + version="7.1.2.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.2" match="equivalent"/> - <import feature="org.eclipse.jgit.lfs" version="7.0.2" match="equivalent"/> - <import feature="org.eclipse.jgit.ssh.apache" version="7.0.2" match="equivalent"/> + <import feature="org.eclipse.jgit" version="7.1.2" match="equivalent"/> + <import feature="org.eclipse.jgit.lfs" version="7.1.2" match="equivalent"/> + <import feature="org.eclipse.jgit.ssh.apache" version="7.1.2" 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 bb595d6..949a9c8 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId>
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 2c890e2..adf4e15 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.2-SNAPSHOT</version> + <version>7.1.2-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 79d09f7..b6b5339 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.2.qualifier" + version="7.1.2.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.2" match="equivalent"/> + <import feature="org.eclipse.jgit" version="7.1.2" 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 51bab0c..d67b629 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.2-SNAPSHOT</version> + <version>7.1.2-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.2-SNAPSHOT</version> + <version>7.1.2-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 c84de5d..6546e46 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.2.qualifier" + version="7.1.2.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.2" match="equivalent"/> + <import feature="org.eclipse.jgit" version="7.1.2" 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 b135ca0..aa071c9 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.2-SNAPSHOT</version> + <version>7.1.2-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 52d0f3d..f795e44 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.2.qualifier" + version="7.1.2.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.2" match="equivalent"/> + <import plugin="org.eclipse.jgit" version="7.1.2" 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 fb42902..b93d527 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId>
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 index c1964c9..efae5a1 100644 --- 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
@@ -1,7 +1,7 @@ <?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="1729714537"> +<target name="jgit-4.32" sequenceNumber="1731963692"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/> @@ -79,7 +79,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> - <version>5.12.0</version> + <version>5.14.2</version> <type>jar</type> </dependency> </dependencies> @@ -89,13 +89,13 @@ <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> - <version>5.14.0</version> + <version>5.15.0</version> <type>jar</type> </dependency> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna-platform</artifactId> - <version>5.14.0</version> + <version>5.15.0</version> <type>jar</type> </dependency> </dependencies> @@ -105,49 +105,49 @@ <dependency> <groupId>org.eclipse.jetty.ee10</groupId> <artifactId>jetty-ee10-servlet</artifactId> - <version>12.0.10</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-http</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-io</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-security</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-session</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-util</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-util-ajax</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> @@ -193,13 +193,13 @@ <dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy</artifactId> - <version>1.15.0</version> + <version>1.15.10</version> <type>jar</type> </dependency> <dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy-agent</artifactId> - <version>1.15.0</version> + <version>1.15.10</version> <type>jar</type> </dependency> </dependencies> @@ -209,25 +209,25 @@ <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpg-jdk18on</artifactId> - <version>1.78.1</version> + <version>1.79</version> <type>jar</type> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> - <version>1.78.1</version> + <version>1.79</version> <type>jar</type> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk18on</artifactId> - <version>1.78.1</version> + <version>1.79</version> <type>jar</type> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcutil-jdk18on</artifactId> - <version>1.78.1</version> + <version>1.79</version> <type>jar</type> </dependency> </dependencies> @@ -269,13 +269,13 @@ <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> - <version>3.16.0</version> + <version>3.17.0</version> <type>jar</type> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.16.1</version> + <version>2.17.0</version> <type>jar</type> </dependency> <dependency>
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 index 4d9f630..1578e4c 100644 --- 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
@@ -1,7 +1,7 @@ <?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="1729714538"> +<target name="jgit-4.33" sequenceNumber="1731963694"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/> @@ -30,7 +30,7 @@ </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-09/"/> + <repository location="https://download.eclipse.org/releases/2024-09/"/> </location> <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz"> <dependencies> @@ -79,7 +79,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> - <version>5.12.0</version> + <version>5.14.2</version> <type>jar</type> </dependency> </dependencies> @@ -89,13 +89,13 @@ <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> - <version>5.14.0</version> + <version>5.15.0</version> <type>jar</type> </dependency> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna-platform</artifactId> - <version>5.14.0</version> + <version>5.15.0</version> <type>jar</type> </dependency> </dependencies> @@ -105,49 +105,49 @@ <dependency> <groupId>org.eclipse.jetty.ee10</groupId> <artifactId>jetty-ee10-servlet</artifactId> - <version>12.0.10</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-http</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-io</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-security</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-session</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-util</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-util-ajax</artifactId> - <version>12.0.12</version> + <version>12.0.15</version> <type>jar</type> </dependency> <dependency> @@ -193,13 +193,13 @@ <dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy</artifactId> - <version>1.15.0</version> + <version>1.15.10</version> <type>jar</type> </dependency> <dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy-agent</artifactId> - <version>1.15.0</version> + <version>1.15.10</version> <type>jar</type> </dependency> </dependencies> @@ -209,25 +209,25 @@ <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpg-jdk18on</artifactId> - <version>1.78.1</version> + <version>1.79</version> <type>jar</type> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> - <version>1.78.1</version> + <version>1.79</version> <type>jar</type> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk18on</artifactId> - <version>1.78.1</version> + <version>1.79</version> <type>jar</type> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcutil-jdk18on</artifactId> - <version>1.78.1</version> + <version>1.79</version> <type>jar</type> </dependency> </dependencies> @@ -269,13 +269,13 @@ <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> - <version>3.16.0</version> + <version>3.17.0</version> <type>jar</type> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.16.1</version> + <version>2.17.0</version> <type>jar</type> </dependency> <dependency>
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 index d01a8a9..74c6878 100644 --- 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
@@ -3,6 +3,6 @@ include "orbit/orbit-4.33.tpd" include "maven/dependencies.tpd" -location "https://download.eclipse.org/staging/2024-09/" { +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 0000000..43678e7 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.target
@@ -0,0 +1,290 @@ +<?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="1731963696"> + <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.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.14.0</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-sftp</artifactId> + <version>2.14.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.14.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.15.0</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>net.java.dev.jna</groupId> + <artifactId>jna-platform</artifactId> + <version>5.15.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.15</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-http</artifactId> + <version>12.0.15</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-io</artifactId> + <version>12.0.15</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-security</artifactId> + <version>12.0.15</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-server</artifactId> + <version>12.0.15</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-session</artifactId> + <version>12.0.15</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-util</artifactId> + <version>12.0.15</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-util-ajax</artifactId> + <version>12.0.15</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.11.0</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.15.10</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>net.bytebuddy</groupId> + <artifactId>byte-buddy-agent</artifactId> + <version>1.15.10</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.79</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcprov-jdk18on</artifactId> + <version>1.79</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcpkix-jdk18on</artifactId> + <version>1.79</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcutil-jdk18on</artifactId> + <version>1.79</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.26.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.17.1</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.17.0</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + <version>1.3.4</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 0000000..4c38371 --- /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/maven/dependencies.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd index 42712c0..7b9a397 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
@@ -20,12 +20,12 @@ dependency { groupId = "org.apache.commons" artifactId = "commons-lang3" - version = "3.16.0" + version = "3.17.0" } dependency { groupId = "commons-io" artifactId = "commons-io" - version = "2.16.1" + version = "2.17.0" } dependency { groupId = "commons-logging" @@ -69,22 +69,22 @@ dependency { groupId = "org.bouncycastle" artifactId = "bcpg-jdk18on" - version = "1.78.1" + version = "1.79" } dependency { groupId = "org.bouncycastle" artifactId = "bcprov-jdk18on" - version = "1.78.1" + version = "1.79" } dependency { groupId = "org.bouncycastle" artifactId = "bcpkix-jdk18on" - version = "1.78.1" + version = "1.79" } dependency { groupId = "org.bouncycastle" artifactId = "bcutil-jdk18on" - version = "1.78.1" + version = "1.79" } } @@ -97,12 +97,12 @@ dependency { groupId = "net.bytebuddy" artifactId = "byte-buddy" - version = "1.15.0" + version = "1.15.10" } dependency { groupId = "net.bytebuddy" artifactId = "byte-buddy-agent" - version = "1.15.0" + version = "1.15.10" } } @@ -154,42 +154,42 @@ dependency { groupId = "org.eclipse.jetty.ee10" artifactId = "jetty-ee10-servlet" - version = "12.0.10" + version = "12.0.15" } dependency { groupId = "org.eclipse.jetty" artifactId = "jetty-http" - version = "12.0.12" + version = "12.0.15" } dependency { groupId = "org.eclipse.jetty" artifactId = "jetty-io" - version = "12.0.12" + version = "12.0.15" } dependency { groupId = "org.eclipse.jetty" artifactId = "jetty-security" - version = "12.0.12" + version = "12.0.15" } dependency { groupId = "org.eclipse.jetty" artifactId = "jetty-server" - version = "12.0.12" + version = "12.0.15" } dependency { groupId = "org.eclipse.jetty" artifactId = "jetty-session" - version = "12.0.12" + version = "12.0.15" } dependency { groupId = "org.eclipse.jetty" artifactId = "jetty-util" - version = "12.0.12" + version = "12.0.15" } dependency { groupId = "org.eclipse.jetty" artifactId = "jetty-util-ajax" - version = "12.0.12" + version = "12.0.15" } dependency { groupId = "jakarta.servlet" @@ -207,12 +207,12 @@ dependency { groupId = "net.java.dev.jna" artifactId = "jna" - version = "5.14.0" + version = "5.15.0" } dependency { groupId = "net.java.dev.jna" artifactId = "jna-platform" - version = "5.14.0" + version = "5.15.0" } } @@ -225,7 +225,7 @@ dependency { groupId = "org.mockito" artifactId = "mockito-core" - version = "5.12.0" + version = "5.14.2" } }
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 0000000..15931db --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.34.tpd
@@ -0,0 +1,27 @@ +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] + 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.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 5d750fb..37fe6e9 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> <packaging>pom</packaging> <name>JGit Tycho Parent</name>
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF index 339b969..7d3bc52 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-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.pgm.test Bundle-SymbolicName: org.eclipse.jgit.pgm.test -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-17 -Import-Package: org.eclipse.jgit.api;version="[7.0.2,7.1.0)", - org.eclipse.jgit.api.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.diff;version="[7.0.2,7.1.0)", - org.eclipse.jgit.dircache;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.diffmergetool;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib.internal;version="[7.0.2,7.1.0)", - org.eclipse.jgit.merge;version="[7.0.2,7.1.0)", - org.eclipse.jgit.pgm;version="[7.0.2,7.1.0)", - org.eclipse.jgit.pgm.internal;version="[7.0.2,7.1.0)", - org.eclipse.jgit.pgm.opt;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.treewalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util.io;version="[7.0.2,7.1.0)", +Import-Package: org.eclipse.jgit.api;version="[7.1.2,7.2.0)", + org.eclipse.jgit.api.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.diff;version="[7.1.2,7.2.0)", + org.eclipse.jgit.dircache;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.diffmergetool;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib.internal;version="[7.1.2,7.2.0)", + org.eclipse.jgit.merge;version="[7.1.2,7.2.0)", + org.eclipse.jgit.pgm;version="[7.1.2,7.2.0)", + org.eclipse.jgit.pgm.internal;version="[7.1.2,7.2.0)", + org.eclipse.jgit.pgm.opt;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.treewalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util.io;version="[7.1.2,7.2.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 8009940..cca0d81 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.pgm.test</artifactId>
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 0000000..b4d4ea9 --- /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 0d41cb8..b889686 100644 --- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.pgm Bundle-SymbolicName: org.eclipse.jgit.pgm -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: OSGI-INF/l10n/plugin Bundle-RequiredExecutionEnvironment: JavaSE-17 @@ -14,49 +14,49 @@ 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.2,7.1.0)", - org.eclipse.jgit.api.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.archive;version="[7.0.2,7.1.0)", - org.eclipse.jgit.awtui;version="[7.0.2,7.1.0)", - org.eclipse.jgit.blame;version="[7.0.2,7.1.0)", - org.eclipse.jgit.diff;version="[7.0.2,7.1.0)", - org.eclipse.jgit.dircache;version="[7.0.2,7.1.0)", - org.eclipse.jgit.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.gitrepo;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.diffmergetool;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.io;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.pack;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.reftable;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.server;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.server.fs;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs.server.s3;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.merge;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib.internal;version="[7.0.2,7.1.0)", - org.eclipse.jgit.nls;version="[7.0.2,7.1.0)", - org.eclipse.jgit.notes;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revplot;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk.filter;version="[7.0.2,7.1.0)", - org.eclipse.jgit.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.storage.pack;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.http.apache;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.resolver;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.ssh.jsch;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.sshd;version="[7.0.2,7.1.0)", - org.eclipse.jgit.treewalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.treewalk.filter;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util.io;version="[7.0.2,7.1.0)", + org.eclipse.jgit.api;version="[7.1.2,7.2.0)", + org.eclipse.jgit.api.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.archive;version="[7.1.2,7.2.0)", + org.eclipse.jgit.awtui;version="[7.1.2,7.2.0)", + org.eclipse.jgit.blame;version="[7.1.2,7.2.0)", + org.eclipse.jgit.diff;version="[7.1.2,7.2.0)", + org.eclipse.jgit.dircache;version="[7.1.2,7.2.0)", + org.eclipse.jgit.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.gitrepo;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.diffmergetool;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.io;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.pack;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.reftable;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.server;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.server.fs;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs.server.s3;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.merge;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib.internal;version="[7.1.2,7.2.0)", + org.eclipse.jgit.nls;version="[7.1.2,7.2.0)", + org.eclipse.jgit.notes;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revplot;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk.filter;version="[7.1.2,7.2.0)", + org.eclipse.jgit.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.storage.pack;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.http.apache;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.resolver;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.ssh.jsch;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.sshd;version="[7.1.2,7.2.0)", + org.eclipse.jgit.treewalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.treewalk.filter;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util.io;version="[7.1.2,7.2.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.2"; +Export-Package: org.eclipse.jgit.console;version="7.1.2"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util", - org.eclipse.jgit.pgm;version="7.0.2"; + org.eclipse.jgit.pgm;version="7.1.2"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util.io, org.eclipse.jgit.awtui, @@ -68,14 +68,14 @@ org.eclipse.jgit.treewalk, org.eclipse.jgit.api, javax.swing", - org.eclipse.jgit.pgm.debug;version="7.0.2"; + org.eclipse.jgit.pgm.debug;version="7.1.2"; uses:="org.eclipse.jgit.util.io, org.eclipse.jgit.pgm, org.eclipse.jetty.servlet", - org.eclipse.jgit.pgm.internal;version="7.0.2"; + org.eclipse.jgit.pgm.internal;version="7.1.2"; x-friends:="org.eclipse.jgit.pgm.test, org.eclipse.jgit.test", - org.eclipse.jgit.pgm.opt;version="7.0.2"; + org.eclipse.jgit.pgm.opt;version="7.1.2"; 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 7b5009f..9acb609 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-Name: org.eclipse.jgit.pgm - Sources Bundle-SymbolicName: org.eclipse.jgit.pgm.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="7.1.2.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 08d3727..41b0091 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,7 @@ org.eclipse.jgit.pgm.Merge org.eclipse.jgit.pgm.MergeBase org.eclipse.jgit.pgm.MergeTool +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 a44e19c..b1baa16 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.2-SNAPSHOT</version> + <version>7.1.2-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 50ee809..d24b639 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
@@ -257,6 +257,7 @@ 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_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". @@ -300,6 +301,7 @@ usage_MergeBase=Find as good common ancestors as possible for a merge usage_MergesTwoDevelopmentHistories=Merges two development histories 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/MergeBase.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java index aacde2f..a29c4d9 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/PackRefs.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/PackRefs.java new file mode 100644 index 0000000..ee05f5c --- /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/debug/ShowPackDelta.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java index 190cbd7..80d3503 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.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 @@ protected void run() throws Exception { 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 boolean select(ObjectToPack otp, StoredObjectRepresentation next) { otp.select(next);
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 0cdec4d..46319c9 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 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent;singleton:=true -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Localization: OSGI-INF/l10n/agent Bundle-Vendor: %Bundle-Vendor -Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[7.0.2,7.1.0)" +Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[7.1.2,7.2.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.2,7.1.0)", - org.eclipse.jgit.nls;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)" +Import-Package: org.eclipse.jgit.transport.sshd;version="[7.1.2,7.2.0)", + org.eclipse.jgit.nls;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.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.2";x-internal:=true +Export-Package: org.eclipse.jgit.internal.transport.sshd.agent.connector;version="7.1.2";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 d4fea05..773bd02 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-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.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache.agent/pom.xml b/org.eclipse.jgit.ssh.apache.agent/pom.xml index 9a44834..85ac2a9 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.2-SNAPSHOT</version> + <version>7.1.2-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 6fdb99a..5be47af 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 0000000..b5b9375 --- /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 b384464..dfc059f 100644 --- a/org.eclipse.jgit.ssh.apache.test/BUILD +++ b/org.eclipse.jgit.ssh.apache.test/BUILD
@@ -1,19 +1,49 @@ load( + "@com_googlesource_gerrit_bazlets//tools:genrule2.bzl", + "genrule2", +) +load( "@com_googlesource_gerrit_bazlets//tools:junit.bzl", "junit_tests", ) +DEPS = [ + "//lib:eddsa", + "//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 cc382fb..21619de 100644 --- a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ 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.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-17 @@ -17,22 +17,28 @@ org.apache.sshd.common.keyprovider;version="[2.14.0,2.15.0)", org.apache.sshd.common.session;version="[2.14.0,2.15.0)", org.apache.sshd.common.signature;version="[2.14.0,2.15.0)", + org.apache.sshd.common.util.buffer;version="[2.14.0,2.15.0)", org.apache.sshd.common.util.net;version="[2.14.0,2.15.0)", org.apache.sshd.common.util.security;version="[2.14.0,2.15.0)", org.apache.sshd.core;version="[2.14.0,2.15.0)", org.apache.sshd.server;version="[2.14.0,2.15.0)", org.apache.sshd.server.forward;version="[2.14.0,2.15.0)", - org.eclipse.jgit.api;version="[7.0.2,7.1.0)", - org.eclipse.jgit.api.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.transport.sshd.proxy;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit.ssh;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.sshd;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.sshd.agent;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", + org.eclipse.jgit.api;version="[7.1.2,7.2.0)", + org.eclipse.jgit.api.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.signing.ssh;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.transport.sshd.proxy;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit.ssh;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.sshd;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.sshd.agent;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.0)", org.junit;version="[4.13,5.0.0)", org.junit.experimental.theories;version="[4.13,5.0.0)", - org.junit.runner;version="[4.13,5.0.0)" + 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,2.0.0)"
diff --git a/org.eclipse.jgit.ssh.apache.test/pom.xml b/org.eclipse.jgit.ssh.apache.test/pom.xml index 624104d..c53adb7 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.2-SNAPSHOT</version> + <version>7.1.2-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 0000000..ec74409 --- /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 0000000..b8de8c3 --- /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 0000000..842415b --- /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 0000000..a4af047 --- /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 0000000..e46c87e --- /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 0000000..9da63ec --- /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 0000000..101e374 --- /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 0000000..752fee1 --- /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 0000000..15825f6 --- /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 0000000..a2b241c --- /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 0000000..5f7164a --- /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 0000000..9469340 --- /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 0000000..6f744c3 --- /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 0000000..84a8bc6 --- /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 0000000..26f29b2 --- /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 0000000..78e5187 --- /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 0000000..cdd1351 --- /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 0000000..1a65243 --- /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 0000000..9ba549f --- /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 0000000..8dd496d --- /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 0000000..9965e2e --- /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 0000000..aefd2b1 --- /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 0000000..3928543 --- /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 0000000..cdd1351 --- /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 0000000..77ddd5e --- /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 0000000..893fd5e --- /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 0000000..e2bcd25 --- /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 0000000..f561982 --- /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 0000000..e50a4fe --- /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 0000000..8e92fa7 --- /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 0000000..1d7fe7f --- /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 0000000..fb457df --- /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 0000000..9492f88 --- /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 0000000..37a0d84 --- /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 0000000..b02e9df --- /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 0000000..90bb86f --- /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 0000000..f3ad249 --- /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 0000000..efa3d5e --- /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 0000000..26e61e0 --- /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 0000000..e51b88c --- /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 0000000..900d444 --- /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 0000000..0709618 --- /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 0000000..13d1aa4 --- /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 0000000..a58675e --- /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 0000000..1431af3 --- /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 0000000..33ad644 --- /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 0000000..630316c --- /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 0000000..b290943 --- /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 0000000..fc13d37 --- /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 0000000..5e671b4 --- /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 0000000..f529a91 --- /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 0000000..e09316a --- /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 0000000..8edd736 --- /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 0000000..80312fb --- /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 0000000..1f0556c --- /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 0000000..f05a1e4 --- /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 0000000..4aedb77 --- /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 0000000..c837fe0 --- /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 0000000..47e01fb --- /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 0000000..2b92f89 --- /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 0000000..770ceee --- /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 0000000..a177fd0 --- /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 0000000..c6f2361 --- /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 0000000..592ddb4 --- /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 0000000..b20fec2 --- /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 0000000..475e90c --- /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 0000000..13109e9 --- /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 0000000..d82a0b5 --- /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 0000000..59ea422 --- /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 0000000..081ac6c --- /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 0000000..9479498 --- /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 0000000..9ee8890 --- /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 0000000..74a797b --- /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 0000000..6fa4fd9 --- /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 0000000..bb954f9 --- /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 0000000..4a866e4 --- /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 0000000..62d5027 --- /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 0000000..367e4ab --- /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 0000000..47cac1e --- /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 0000000..589daa6 --- /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 0000000..1bf3883 --- /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 0000000..4072d92 --- /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 0000000..b5788a0 --- /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 0000000..587cf62 --- /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 0000000..07d5369 --- /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 0000000..52d3283 --- /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 0000000..5b4bd11 --- /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 0000000..88867e5 --- /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 0000000..8f59be9 --- /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 0000000..a6e76f1 --- /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 0000000..5a3acbb --- /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 0000000..9684d72 --- /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 0000000..ab47a2b --- /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 0000000..3a41f29 --- /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 0000000..89df717 --- /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 0000000..2d0fe53 --- /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 0000000..05c5eac --- /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 0000000..38b8232 --- /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 0000000..4671638 --- /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 0000000..0809077 --- /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 0000000..ee3f922 --- /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 0000000..2be08be --- /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 0000000..0255005 --- /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 0000000..c402f54 --- /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 0000000..3dd37be --- /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 0000000..de191d1 --- /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 0000000..e1210e7 --- /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 0000000..fdfffce --- /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 0000000..90fde3f --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AllowedSignersParseTest.java
@@ -0,0 +1,201 @@ +/* + * 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")); + } + + @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( + "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 0000000..9f9c3ca --- /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 0000000..2fd7756 --- /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 0000000..e6709ad --- /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 0000000..79ca21f --- /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 0000000..e5dfe49 --- /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 0000000..b3a4482 --- /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 0000000..30ddee5 --- /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/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF index b7ea87f..d12eed0 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-Vendor: %Bundle-Vendor Bundle-Localization: OSGI-INF/l10n/plugin Bundle-ActivationPolicy: lazy -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-17 -Export-Package: org.eclipse.jgit.internal.transport.sshd;version="7.0.2";x-internal:=true; +Export-Package: org.eclipse.jgit.internal.signing.ssh;version="7.1.2";x-friends:="org.eclipse.jgit.ssh.apache.test", + org.eclipse.jgit.internal.transport.sshd;version="7.1.2";x-internal:=true; uses:="org.apache.sshd.client, org.apache.sshd.client.auth, org.apache.sshd.client.auth.keyboard, @@ -23,18 +24,19 @@ 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.2";x-internal:=true, - org.eclipse.jgit.internal.transport.sshd.auth;version="7.0.2";x-internal:=true, - org.eclipse.jgit.internal.transport.sshd.pkcs11;version="7.0.2";x-internal:=true, - org.eclipse.jgit.internal.transport.sshd.proxy;version="7.0.2";x-friends:="org.eclipse.jgit.ssh.apache.test", - org.eclipse.jgit.transport.sshd;version="7.0.2"; + org.eclipse.jgit.internal.transport.sshd.agent;version="7.1.2";x-internal:=true, + org.eclipse.jgit.internal.transport.sshd.auth;version="7.1.2";x-internal:=true, + org.eclipse.jgit.internal.transport.sshd.pkcs11;version="7.1.2";x-internal:=true, + org.eclipse.jgit.internal.transport.sshd.proxy;version="7.1.2";x-friends:="org.eclipse.jgit.ssh.apache.test", + org.eclipse.jgit.signing.ssh;version="7.1.2";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.transport.sshd;version="7.1.2"; 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.2", + org.eclipse.jgit.transport.sshd.agent;version="7.1.2", sun.security.x509 Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)", org.apache.sshd.agent;version="[2.14.0,2.15.0)", @@ -89,12 +91,14 @@ org.apache.sshd.sftp;version="[2.14.0,2.15.0)", org.apache.sshd.sftp.client;version="[2.14.0,2.15.0)", org.apache.sshd.sftp.common;version="[2.14.0,2.15.0)", - org.eclipse.jgit.annotations;version="[7.0.2,7.1.0)", - org.eclipse.jgit.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.fnmatch;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.transport.ssh;version="[7.0.2,7.1.0)", - org.eclipse.jgit.nls;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", + org.eclipse.jgit.annotations;version="[7.1.2,7.2.0)", + org.eclipse.jgit.api.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.fnmatch;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.transport.ssh;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.nls;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.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 c328382..1a63852 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-Name: org.eclipse.jgit.ssh.apache - Sources Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml index c189776..857f8d6 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
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 0000000..4a0f553 --- /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 0000000..80f22c0 --- /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 7da7181..6048239 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
@@ -125,4 +125,69 @@ 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 0000000..cfbe7a7 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/AllowedSigners.java
@@ -0,0 +1,536 @@ +/* + * 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.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.TimeZone; +import java.util.concurrent.TimeUnit; +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 String SSH_KEY_PREFIX = "ssh-"; //$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) + || matches(line, SSH_KEY_PREFIX, 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); + if (!keyType.startsWith(SSH_KEY_PREFIX)) { + 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(); + } + TimeZone tz = SystemReader.getInstance().getTimeZone(); + // Since there are a few TimeZone IDs that are not recognized by ZoneId, + // use offsets. + return time.atOffset(ZoneOffset.ofTotalSeconds( + (int) TimeUnit.MILLISECONDS.toSeconds(tz.getRawOffset()))) + .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 0000000..6b19eb32 --- /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 0000000..7993def --- /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 0000000..aa26886 --- /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 0000000..f4eb884 --- /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 0000000..e2e1a36 --- /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 0000000..040c6d4 --- /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 0000000..bc72196 --- /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 0000000..76be340 --- /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 0000000..8cfe5f4 --- /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/PasswordProviderWrapper.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java index 2cd0669..900c9fb 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 @@ private static class PerSessionState { private final Supplier<KeyPasswordProvider> factory; + private PerSessionState noSessionState; + /** * Creates a new {@link PasswordProviderWrapper}. * @@ -59,13 +61,18 @@ public PasswordProviderWrapper( } 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 05f04ac..0533b65 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 @@ -147,6 +147,71 @@ public static SshdText get() { /***/ 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/signing/ssh/CachingSigningKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/CachingSigningKeyDatabase.java new file mode 100644 index 0000000..4d2d8b6 --- /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 0000000..eec64c3 --- /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 0000000..c315428 --- /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 0000000..5459b53 --- /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 0000000..cd77111 --- /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 0000000..0537300 --- /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 2c3cbe5..4a2eb9c 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 SshdSession getSession(URIish uri, 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 @@ protected final KeyCache getKeyCache() { * @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.jsch.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF index 8821702..71977c6 100644 --- a/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
@@ -3,20 +3,20 @@ 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.2.qualifier +Bundle-Version: 7.1.2.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.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit.ssh;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.ssh.jsch;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", + org.eclipse.jgit.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit.ssh;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.ssh.jsch;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.0)", org.hamcrest;version="[1.1.0,3.0.0)", org.junit;version="[4.13,5.0.0)", org.junit.experimental.theories;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 4c9097a..f67b1bd 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ssh.jsch.test</artifactId>
diff --git a/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF index 92840a9..3a6e08c 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-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.2,7.1.0)" +Fragment-Host: org.eclipse.jgit;bundle-version="[7.1.2,7.2.0)" Bundle-Vendor: %Bundle-Vendor Bundle-Localization: OSGI-INF/l10n/jsch Bundle-ActivationPolicy: lazy -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-17 -Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="7.0.2" +Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="7.1.2" Import-Package: com.jcraft.jsch;version="[0.1.37,0.2.0)", - org.eclipse.jgit.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.transport.ssh;version="[7.0.2,7.1.0)", - org.eclipse.jgit.nls;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util.io;version="[7.0.2,7.1.0)", + org.eclipse.jgit.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.transport.ssh;version="[7.1.2,7.2.0)", + org.eclipse.jgit.nls;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util.io;version="[7.1.2,7.2.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 c0e1acc..f4c12e6 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-Name: org.eclipse.jgit.ssh.jsch - Sources Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.jsch/pom.xml b/org.eclipse.jgit.ssh.jsch/pom.xml index 1f4564c..4427602 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ssh.jsch</artifactId>
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index eb95243..77a7f39 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.test Bundle-SymbolicName: org.eclipse.jgit.test -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor Bundle-RequiredExecutionEnvironment: JavaSE-17 @@ -21,64 +21,64 @@ org.apache.commons.io;version="[2.15.0,3.0.0)", org.apache.commons.io.output;version="[2.15.0,3.0.0)", org.assertj.core.api;version="[3.14.0,4.0.0)", - org.eclipse.jgit.annotations;version="[7.0.2,7.1.0)", - org.eclipse.jgit.api;version="[7.0.2,7.1.0)", - org.eclipse.jgit.api.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.archive;version="[7.0.2,7.1.0)", - org.eclipse.jgit.attributes;version="[7.0.2,7.1.0)", - org.eclipse.jgit.awtui;version="[7.0.2,7.1.0)", - org.eclipse.jgit.blame;version="[7.0.2,7.1.0)", - org.eclipse.jgit.diff;version="[7.0.2,7.1.0)", - org.eclipse.jgit.dircache;version="[7.0.2,7.1.0)", - org.eclipse.jgit.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.events;version="[7.0.2,7.1.0)", - org.eclipse.jgit.fnmatch;version="[7.0.2,7.1.0)", - org.eclipse.jgit.gitrepo;version="[7.0.2,7.1.0)", - org.eclipse.jgit.hooks;version="[7.0.2,7.1.0)", - org.eclipse.jgit.ignore;version="[7.0.2,7.1.0)", - org.eclipse.jgit.ignore.internal;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.diff;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.diffmergetool;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.fsck;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.commitgraph;version="7.0.2", - org.eclipse.jgit.internal.storage.dfs;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.io;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.memory;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.pack;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.storage.reftable;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.transport.connectivity;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.transport.http;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.transport.parser;version="[7.0.2,7.1.0)", - org.eclipse.jgit.internal.transport.ssh;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit;version="[7.0.2,7.1.0)", - org.eclipse.jgit.junit.time;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lfs;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib.internal;version="[7.0.2,7.1.0)", - org.eclipse.jgit.logging;version="[7.0.2,7.1.0)", - org.eclipse.jgit.merge;version="[7.0.2,7.1.0)", - org.eclipse.jgit.nls;version="[7.0.2,7.1.0)", - org.eclipse.jgit.notes;version="[7.0.2,7.1.0)", - org.eclipse.jgit.patch;version="[7.0.2,7.1.0)", - org.eclipse.jgit.pgm;version="[7.0.2,7.1.0)", - org.eclipse.jgit.pgm.internal;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revplot;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk.filter;version="[7.0.2,7.1.0)", - org.eclipse.jgit.storage.file;version="[7.0.2,7.1.0)", - org.eclipse.jgit.storage.pack;version="[7.0.2,7.1.0)", - org.eclipse.jgit.submodule;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.http;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport.resolver;version="[7.0.2,7.1.0)", - org.eclipse.jgit.treewalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.treewalk.filter;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util.io;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util.sha1;version="[7.0.2,7.1.0)", + org.eclipse.jgit.annotations;version="[7.1.2,7.2.0)", + org.eclipse.jgit.api;version="[7.1.2,7.2.0)", + org.eclipse.jgit.api.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.archive;version="[7.1.2,7.2.0)", + org.eclipse.jgit.attributes;version="[7.1.2,7.2.0)", + org.eclipse.jgit.awtui;version="[7.1.2,7.2.0)", + org.eclipse.jgit.blame;version="[7.1.2,7.2.0)", + org.eclipse.jgit.diff;version="[7.1.2,7.2.0)", + org.eclipse.jgit.dircache;version="[7.1.2,7.2.0)", + org.eclipse.jgit.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.events;version="[7.1.2,7.2.0)", + org.eclipse.jgit.fnmatch;version="[7.1.2,7.2.0)", + org.eclipse.jgit.gitrepo;version="[7.1.2,7.2.0)", + org.eclipse.jgit.hooks;version="[7.1.2,7.2.0)", + org.eclipse.jgit.ignore;version="[7.1.2,7.2.0)", + org.eclipse.jgit.ignore.internal;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.diff;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.diffmergetool;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.fsck;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.commitgraph;version="7.1.2", + org.eclipse.jgit.internal.storage.dfs;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.io;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.memory;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.pack;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.storage.reftable;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.transport.connectivity;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.transport.http;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.transport.parser;version="[7.1.2,7.2.0)", + org.eclipse.jgit.internal.transport.ssh;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit;version="[7.1.2,7.2.0)", + org.eclipse.jgit.junit.time;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lfs;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib.internal;version="[7.1.2,7.2.0)", + org.eclipse.jgit.logging;version="[7.1.2,7.2.0)", + org.eclipse.jgit.merge;version="[7.1.2,7.2.0)", + org.eclipse.jgit.nls;version="[7.1.2,7.2.0)", + org.eclipse.jgit.notes;version="[7.1.2,7.2.0)", + org.eclipse.jgit.patch;version="[7.1.2,7.2.0)", + org.eclipse.jgit.pgm;version="[7.1.2,7.2.0)", + org.eclipse.jgit.pgm.internal;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revplot;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk.filter;version="[7.1.2,7.2.0)", + org.eclipse.jgit.storage.file;version="[7.1.2,7.2.0)", + org.eclipse.jgit.storage.pack;version="[7.1.2,7.2.0)", + org.eclipse.jgit.submodule;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.http;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport.resolver;version="[7.1.2,7.2.0)", + org.eclipse.jgit.treewalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.treewalk.filter;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util.io;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util.sha1;version="[7.1.2,7.2.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/pom.xml b/org.eclipse.jgit.test/pom.xml index d44ca07..479050b 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.test</artifactId>
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 12300b3..6d5e45c 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.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.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 void testPullMerge() throws Exception { + db.getWorkTree().getAbsolutePath(); assertEquals(message, mergeCommit.getShortMessage()); } + assertTrue(target.status().call().isClean()); } @Test @@ -153,6 +156,10 @@ public void testPullConflict() throws Exception { 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/SecurityManagerMissingPermissionsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerMissingPermissionsTest.java deleted file mode 100644 index d0fbdbd..0000000 --- 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 2b930a1..0000000 --- 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/internal/storage/dfs/AggregatedBlockCacheStatsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStatsTest.java new file mode 100644 index 0000000..2c4b432 --- /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 0000000..2e2f86b --- /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 c93f48d..afa3179 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,6 +38,7 @@ 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; @@ -48,11 +49,17 @@ 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; @@ -173,6 +180,108 @@ public void fromConfig_generatesDfsBlockCachePackExtConfigs() { } @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", @@ -216,6 +325,46 @@ public void fromConfigs_dfsBlockCachePackExtConfigWithUnknownExtensions_throws() () -> 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;
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 fef0563..3c7cc07 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 @@ 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.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 @@ private void resetCache() { } 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 2be11d3..f9fbfe8 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.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,8 +16,11 @@ import static org.junit.Assert.fail; import java.io.IOException; +import java.time.Instant; import java.util.Arrays; import java.util.Collections; +import java.util.Date; +import java.util.GregorianCalendar; import java.util.concurrent.TimeUnit; import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph; @@ -24,6 +28,7 @@ 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; @@ -37,6 +42,7 @@ 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; @@ -44,6 +50,7 @@ import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.storage.pack.PackConfig; import org.eclipse.jgit.transport.ReceiveCommand; +import org.eclipse.jgit.util.GitDateParser; import org.eclipse.jgit.util.SystemReader; import org.junit.After; import org.junit.Before; @@ -1275,6 +1282,90 @@ public void bitmapIndexWrittenDuringGc() throws Exception { 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()); + long currentDay = new Date().getTime(); + GregorianCalendar cal = new GregorianCalendar(SystemReader + .getInstance().getTimeZone(), SystemReader.getInstance() + .getLocale()); + long ten_days_ago = GitDateParser.parse("10 days ago",cal,SystemReader.getInstance() + .getLocale()).getTime() ; + long twenty_days_ago = GitDateParser.parse("20 days ago",cal,SystemReader.getInstance() + .getLocale()).getTime() ; + long thirty_days_ago = GitDateParser.parse("30 days ago",cal,SystemReader.getInstance() + .getLocale()).getTime() ;; + long fifty_days_ago = GitDateParser.parse("50 days ago",cal,SystemReader.getInstance() + .getLocale()).getTime() ; + PersonIdent who2 = new PersonIdent("J.Author", "authemail", currentDay, -8 * 60); + PersonIdent who3 = new PersonIdent("J.Author", "authemail", ten_days_ago, -8 * 60); + PersonIdent who4 = new PersonIdent("J.Author", "authemail", twenty_days_ago, -8 * 60); + PersonIdent who5 = new PersonIdent("J.Author", "authemail", thirty_days_ago, -8 * 60); + PersonIdent who6 = new PersonIdent("J.Author", "authemail", fifty_days_ago, -8 * 60); + + 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(Instant.ofEpochMilli(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++) { @@ -1364,4 +1455,12 @@ private int countPacks(PackSource source) throws IOException { } 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/DfsPackCompacterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java index c516e30..c3b6aa8 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 @@ 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 void testEstimateGcPackSizeWithAnExistingGcPack() throws Exception { 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 @@ private void compact() throws IOException { 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 bc851f8..9680019 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.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; @@ -309,7 +310,7 @@ private ObjectId setupPack(int bs, int ps) throws IOException { 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/PackExtBlockCacheTableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTableTest.java index 8c003e0..e7627bc 100644 --- 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
@@ -10,7 +10,10 @@ 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; @@ -30,13 +33,14 @@ 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.dfs.DfsBlockCacheTable.DfsBlockCacheStats; 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(); @@ -385,6 +389,65 @@ public void get_packExtDoesNotMapToCacheTable_callsDefaultCache() { } @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(); @@ -406,7 +469,8 @@ public void getBlockCacheStats_getCurrentSize_consolidatesAllTableCurrentSizes() cacheTableWithStats(bitmapStats), PackExt.INDEX, cacheTableWithStats(indexStats))); - assertArrayEquals(tables.getBlockCacheStats().getCurrentSize(), + assertArrayEquals(AggregatedBlockCacheStats + .fromStatsList(tables.getBlockCacheStats()).getCurrentSize(), currentSizes); } @@ -435,7 +499,9 @@ public void getBlockCacheStats_GetHitCount_consolidatesAllTableHitCounts() { cacheTableWithStats(bitmapStats), PackExt.INDEX, cacheTableWithStats(indexStats))); - assertArrayEquals(tables.getBlockCacheStats().getHitCount(), hitCounts); + assertArrayEquals(AggregatedBlockCacheStats + .fromStatsList(tables.getBlockCacheStats()).getHitCount(), + hitCounts); } @Test @@ -463,7 +529,8 @@ public void getBlockCacheStats_getMissCount_consolidatesAllTableMissCounts() { cacheTableWithStats(bitmapStats), PackExt.INDEX, cacheTableWithStats(indexStats))); - assertArrayEquals(tables.getBlockCacheStats().getMissCount(), + assertArrayEquals(AggregatedBlockCacheStats + .fromStatsList(tables.getBlockCacheStats()).getMissCount(), missCounts); } @@ -498,8 +565,9 @@ public void getBlockCacheStats_getTotalRequestCount_consolidatesAllTableTotalReq cacheTableWithStats(bitmapStats), PackExt.INDEX, cacheTableWithStats(indexStats))); - assertArrayEquals(tables.getBlockCacheStats().getTotalRequestCount(), - totalRequestCounts); + assertArrayEquals(AggregatedBlockCacheStats + .fromStatsList(tables.getBlockCacheStats()) + .getTotalRequestCount(), totalRequestCounts); } @Test @@ -529,7 +597,9 @@ public void getBlockCacheStats_getHitRatio_consolidatesAllTableHitRatios() { cacheTableWithStats(bitmapStats), PackExt.INDEX, cacheTableWithStats(indexStats))); - assertArrayEquals(tables.getBlockCacheStats().getHitRatio(), hitRatios); + assertArrayEquals(AggregatedBlockCacheStats + .fromStatsList(tables.getBlockCacheStats()).getHitRatio(), + hitRatios); } @Test @@ -557,10 +627,21 @@ public void getBlockCacheStats_getEvictions_consolidatesAllTableEvictions() { cacheTableWithStats(bitmapStats), PackExt.INDEX, cacheTableWithStats(indexStats))); - assertArrayEquals(tables.getBlockCacheStats().getEvictions(), + 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(); @@ -572,9 +653,16 @@ private static void incrementCounter(int amount, Runnable fn) { } private static DfsBlockCacheTable cacheTableWithStats( - DfsBlockCacheStats dfsBlockCacheStats) { + BlockCacheStats dfsBlockCacheStats) { + return cacheTableWithStats(CACHE_NAME, dfsBlockCacheStats); + } + + private static DfsBlockCacheTable cacheTableWithStats(String name, + BlockCacheStats dfsBlockCacheStats) { DfsBlockCacheTable cacheTable = mock(DfsBlockCacheTable.class); - when(cacheTable.getBlockCacheStats()).thenReturn(dfsBlockCacheStats); + when(cacheTable.getName()).thenReturn(name); + when(cacheTable.getBlockCacheStats()) + .thenReturn(List.of(dfsBlockCacheStats)); return cacheTable; }
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 bd36337..41a33df 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 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 similarity index 99% rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BasePackWriterTest.java index ad2c891..cd73c6a 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.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/GcNumberOfPackFilesSinceBitmapStatisticsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcNumberOfPackFilesSinceBitmapStatisticsTest.java new file mode 100644 index 0000000..cd1264e --- /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 c572955..f84be21 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.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.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 void looseRefPacked() throws Exception { RevBlob a = tr.blob("a"); tr.lightweightTag("t", a); - gc.packRefs(); + packRefs(false); assertSame(repo.exactRef("refs/tags/t").getStorage(), Storage.PACKED); } @@ -60,7 +61,7 @@ public void emptyRefDirectoryDeleted() throws Exception { String name = repo.findRef(ref).getName(); Path dir = repo.getCommonDirectory().toPath().resolve(name).getParent(); assertNotNull(dir); - gc.packRefs(); + packRefs(true); assertFalse(Files.exists(dir)); } @@ -75,9 +76,9 @@ public void concurrentOnlyOneWritesPackedRefs() throws Exception { 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 void whileRefLockedRefNotPackedNoError() "refs/tags/t1")); try { refLock.lock(); - gc.packRefs(); + packRefs(false); } finally { refLock.unlock(); } @@ -145,7 +146,7 @@ public boolean isForceUpdate() { Future<Result> result2 = pool.submit(() -> { refUpdateLockedRef.await(); - gc.packRefs(); + packRefs(false); packRefsDone.await(); return null; }); @@ -173,19 +174,20 @@ public void dontPackHEAD_nonBare() throws Exception { 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 void dontPackHEAD_bare() throws Exception { 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 void dontPackHEAD_bare() throws Exception { // 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/T0003_BasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java index 49e8a7b..e067beb 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.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 void test008_FailOnWrongVersion() throws IOException { 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 void test020_createBlobTag() throws IOException { 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 void test020_createBlobTag() throws IOException { 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 void test021_createTreeTag() throws IOException { 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 void test021_createTreeTag() throws IOException { 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 void test022_createCommitTag() throws IOException { 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 void test022_createCommitTag() throws IOException { 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 void test023_createCommitNonAnullii() throws IOException { 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 void test024_createCommitNonAscii() throws IOException { 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 void test026_CreateCommitMultipleparents() throws IOException { .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 void test026_CreateCommitMultipleparents() throws IOException { 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 void test026_CreateCommitMultipleparents() throws IOException { 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 void test026_CreateCommitMultipleparents() throws IOException { 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/lib/PersonIdentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PersonIdentTest.java index 97da175..943a68b 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 void testNewIdentInstant() { 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 void testNewIdentInstant2() { 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/merge/MergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java index 3a036ac..c6a6321 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 void checkModeMergeConflictInVirtualAncestor(MergeStrategy strategy) thro // 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/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java index 2bd4003..aaecfd2 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
@@ -2862,7 +2862,7 @@ public void testSingleBranchCloneTagChain() throws Exception { 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); @@ -2872,7 +2872,6 @@ public void testSingleBranchCloneTagChain() throws Exception { clientWant.writeString("done\n"); try (ByteArrayOutputStream serverResponse = new ByteArrayOutputStream()) { - uploadPack.setPreUploadHook(new PreUploadHook() { @Override public void onBeginNegotiateRound(UploadPack up, @@ -2925,6 +2924,7 @@ public void onSendPack(UploadPack up, assertTrue(objDb.has(heavyTag2.toObjectId())); } } +} @Test public void testSingleBranchShallowCloneTagChainWithReflessTag() throws Exception { @@ -2936,7 +2936,7 @@ public void testSingleBranchShallowCloneTagChainWithReflessTag() throws Exceptio 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); @@ -2946,7 +2946,6 @@ public void testSingleBranchShallowCloneTagChainWithReflessTag() throws Exceptio clientWant.writeString("done\n"); try (ByteArrayOutputStream serverResponse = new ByteArrayOutputStream()) { - uploadPack.setPreUploadHook(new PreUploadHook() { @Override public void onBeginNegotiateRound(UploadPack up, @@ -2994,6 +2993,7 @@ public void onSendPack(UploadPack up, assertTrue(objDb.has(one.toObjectId())); } } +} @Test public void testSafeToClearRefsInFetchV0() throws Exception {
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 0000000..a59d7bc --- /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 0000000..0e5eb28 --- /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 355bbba..e517889 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 void testParsePersonIdent_legalCases() { @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 void testParsePersonIdent_fuzzyCases() { @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 void testParsePersonIdent_incompleteCases() { 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
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF index 7c98d77..399977a 100644 --- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -4,14 +4,14 @@ Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.ui Bundle-SymbolicName: org.eclipse.jgit.ui -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-RequiredExecutionEnvironment: JavaSE-17 -Export-Package: org.eclipse.jgit.awtui;version="7.0.2" -Import-Package: org.eclipse.jgit.errors;version="[7.0.2,7.1.0)", - org.eclipse.jgit.lib;version="[7.0.2,7.1.0)", - org.eclipse.jgit.nls;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revplot;version="[7.0.2,7.1.0)", - org.eclipse.jgit.revwalk;version="[7.0.2,7.1.0)", - org.eclipse.jgit.transport;version="[7.0.2,7.1.0)", - org.eclipse.jgit.util;version="[7.0.2,7.1.0)" +Export-Package: org.eclipse.jgit.awtui;version="7.1.2" +Import-Package: org.eclipse.jgit.errors;version="[7.1.2,7.2.0)", + org.eclipse.jgit.lib;version="[7.1.2,7.2.0)", + org.eclipse.jgit.nls;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revplot;version="[7.1.2,7.2.0)", + org.eclipse.jgit.revwalk;version="[7.1.2,7.2.0)", + org.eclipse.jgit.transport;version="[7.1.2,7.2.0)", + org.eclipse.jgit.util;version="[7.1.2,7.2.0)"
diff --git a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF index 0d64d01..7ed704c 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-Name: org.eclipse.jgit.ui - Sources Bundle-SymbolicName: org.eclipse.jgit.ui.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ui;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ui;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml index ea73b43..90f74a4 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.2-SNAPSHOT</version> + <version>7.1.2-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 new file mode 100644 index 0000000..023018e --- /dev/null +++ b/org.eclipse.jgit/.settings/.api_filters
@@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<component id="org.eclipse.jgit" version="2"> + <resource path="src/org/eclipse/jgit/diff/DiffDriver.java" type="org.eclipse.jgit.diff.DiffDriver"> + <filter id="1109393411"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="org.eclipse.jgit.diff.DiffDriver"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/diff/DiffFormatter.java" type="org.eclipse.jgit.diff.DiffFormatter"> + <filter id="1142947843"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="format(EditList, RawText, RawText, DiffDriver)"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="format(FileHeader, RawText, RawText, DiffDriver)"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="writeHunkHeader(int, int, int, int, String)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/lib/Constants.java" type="org.eclipse.jgit.lib.Constants"> + <filter id="1142947843"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="ATTR_BUILTIN_UNION_MERGE_DRIVER"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/merge/ContentMergeStrategy.java" type="org.eclipse.jgit.merge.ContentMergeStrategy"> + <filter id="1176502275"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="UNION"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/merge/ResolveMerger.java" type="org.eclipse.jgit.merge.ResolveMerger"> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/> + <message_argument value="attributesNodeProvider"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="attributesNodeProvider"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="setAttributesNodeProvider(AttributesNodeProvider)"/> + </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<RevCommit>)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/transport/UploadPack.java" type="org.eclipse.jgit.transport.UploadPack$RequestPolicy"> + <filter id="1176502275"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="implies(UploadPack.RequestPolicy)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/util/Iterators.java" type="org.eclipse.jgit.util.Iterators"> + <filter id="1109393411"> + <message_arguments> + <message_argument value="6.10.2"/> + <message_argument value="org.eclipse.jgit.util.Iterators"/> + </message_arguments> + </filter> + </resource> +</component>
diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs index c4dc76f..ef3d8ec 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.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 0d4f772..1cce9dc 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@ Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit Bundle-SymbolicName: org.eclipse.jgit -Bundle-Version: 7.0.2.qualifier +Bundle-Version: 7.1.2.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.2", - org.eclipse.jgit.api;version="7.0.2"; +Export-Package: org.eclipse.jgit.annotations;version="7.1.2", + org.eclipse.jgit.api;version="7.1.2"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.notes, org.eclipse.jgit.dircache, @@ -25,18 +25,18 @@ org.eclipse.jgit.revwalk.filter, org.eclipse.jgit.blame, org.eclipse.jgit.merge", - org.eclipse.jgit.api.errors;version="7.0.2"; + org.eclipse.jgit.api.errors;version="7.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.errors", - org.eclipse.jgit.attributes;version="7.0.2"; + org.eclipse.jgit.attributes;version="7.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk", - org.eclipse.jgit.blame;version="7.0.2"; + org.eclipse.jgit.blame;version="7.1.2"; 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.2"; + org.eclipse.jgit.diff;version="7.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.attributes, org.eclipse.jgit.revwalk, @@ -44,53 +44,53 @@ org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.treewalk, org.eclipse.jgit.util", - org.eclipse.jgit.dircache;version="7.0.2"; + org.eclipse.jgit.dircache;version="7.1.2"; 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.2"; + org.eclipse.jgit.errors;version="7.1.2"; 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.2"; + org.eclipse.jgit.events;version="7.1.2"; uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.fnmatch;version="7.0.2", - org.eclipse.jgit.gitrepo;version="7.0.2"; + org.eclipse.jgit.fnmatch;version="7.1.2", + org.eclipse.jgit.gitrepo;version="7.1.2"; uses:="org.xml.sax.helpers, org.eclipse.jgit.api, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.xml.sax", - org.eclipse.jgit.gitrepo.internal;version="7.0.2";x-internal:=true, - org.eclipse.jgit.hooks;version="7.0.2";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.ignore;version="7.0.2", - org.eclipse.jgit.ignore.internal;version="7.0.2"; + org.eclipse.jgit.gitrepo.internal;version="7.1.2";x-internal:=true, + org.eclipse.jgit.hooks;version="7.1.2";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.ignore;version="7.1.2", + org.eclipse.jgit.ignore.internal;version="7.1.2"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal;version="7.0.2"; + org.eclipse.jgit.internal;version="7.1.2"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.http.test", - org.eclipse.jgit.internal.diff;version="7.0.2"; + org.eclipse.jgit.internal.diff;version="7.1.2"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.diffmergetool;version="7.0.2"; + org.eclipse.jgit.internal.diffmergetool;version="7.1.2"; 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.2"; + org.eclipse.jgit.internal.fsck;version="7.1.2"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.revwalk;version="7.0.2"; + org.eclipse.jgit.internal.revwalk;version="7.1.2"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.storage.commitgraph;version="7.0.2"; + org.eclipse.jgit.internal.storage.commitgraph;version="7.1.2"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.storage.dfs;version="7.0.2"; + org.eclipse.jgit.internal.storage.dfs;version="7.1.2"; 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.2"; + org.eclipse.jgit.internal.storage.file;version="7.1.2"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.junit, org.eclipse.jgit.junit.http, @@ -102,36 +102,36 @@ org.eclipse.jgit.ssh.apache, org.eclipse.jgit.ssh.apache.test, org.eclipse.jgit.ssh.jsch.test", - org.eclipse.jgit.internal.storage.io;version="7.0.2"; + org.eclipse.jgit.internal.storage.io;version="7.1.2"; x-friends:="org.eclipse.jgit.junit, org.eclipse.jgit.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.memory;version="7.0.2"; + org.eclipse.jgit.internal.storage.memory;version="7.1.2"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.storage.pack;version="7.0.2"; + org.eclipse.jgit.internal.storage.pack;version="7.1.2"; x-friends:="org.eclipse.jgit.junit, org.eclipse.jgit.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.reftable;version="7.0.2"; + org.eclipse.jgit.internal.storage.reftable;version="7.1.2"; 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.2";x-internal:=true, - org.eclipse.jgit.internal.transport.connectivity;version="7.0.2"; + org.eclipse.jgit.internal.submodule;version="7.1.2";x-internal:=true, + org.eclipse.jgit.internal.transport.connectivity;version="7.1.2"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.http;version="7.0.2"; + org.eclipse.jgit.internal.transport.http;version="7.1.2"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.parser;version="7.0.2"; + org.eclipse.jgit.internal.transport.parser;version="7.1.2"; x-friends:="org.eclipse.jgit.http.server, org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.ssh;version="7.0.2"; + org.eclipse.jgit.internal.transport.ssh;version="7.1.2"; x-friends:="org.eclipse.jgit.ssh.apache, org.eclipse.jgit.ssh.jsch, org.eclipse.jgit.test", - org.eclipse.jgit.internal.util;version="7.0.2"; + org.eclipse.jgit.internal.util;version="7.1.2"; x-friends:=" org.eclipse.jgit.junit", - org.eclipse.jgit.lib;version="7.0.2"; + org.eclipse.jgit.lib;version="7.1.2"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util.sha1, org.eclipse.jgit.dircache, @@ -145,12 +145,12 @@ org.eclipse.jgit.util, org.eclipse.jgit.submodule, org.eclipse.jgit.util.time", - org.eclipse.jgit.lib.internal;version="7.0.2"; + org.eclipse.jgit.lib.internal;version="7.1.2"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.pgm, org.eclipse.egit.ui", - org.eclipse.jgit.logging;version="7.0.2", - org.eclipse.jgit.merge;version="7.0.2"; + org.eclipse.jgit.logging;version="7.1.2", + org.eclipse.jgit.merge;version="7.1.2"; uses:="org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, @@ -159,40 +159,40 @@ org.eclipse.jgit.util, org.eclipse.jgit.api, org.eclipse.jgit.attributes", - org.eclipse.jgit.nls;version="7.0.2", - org.eclipse.jgit.notes;version="7.0.2"; + org.eclipse.jgit.nls;version="7.1.2", + org.eclipse.jgit.notes;version="7.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk, org.eclipse.jgit.merge", - org.eclipse.jgit.patch;version="7.0.2"; + org.eclipse.jgit.patch;version="7.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.diff", - org.eclipse.jgit.revplot;version="7.0.2"; + org.eclipse.jgit.revplot;version="7.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk", - org.eclipse.jgit.revwalk;version="7.0.2"; + org.eclipse.jgit.revwalk;version="7.1.2"; uses:="org.eclipse.jgit.lib, 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.2"; + org.eclipse.jgit.revwalk.filter;version="7.1.2"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.lib, org.eclipse.jgit.util", - org.eclipse.jgit.storage.file;version="7.0.2"; + org.eclipse.jgit.storage.file;version="7.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.util", - org.eclipse.jgit.storage.pack;version="7.0.2"; + org.eclipse.jgit.storage.pack;version="7.1.2"; uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.submodule;version="7.0.2"; + org.eclipse.jgit.submodule;version="7.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.diff, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.treewalk, org.eclipse.jgit.util", - org.eclipse.jgit.transport;version="7.0.2"; + org.eclipse.jgit.transport;version="7.1.2"; uses:="javax.crypto, org.eclipse.jgit.util.io, org.eclipse.jgit.lib, @@ -205,21 +205,21 @@ org.eclipse.jgit.transport.resolver, org.eclipse.jgit.storage.pack, org.eclipse.jgit.errors", - org.eclipse.jgit.transport.http;version="7.0.2"; + org.eclipse.jgit.transport.http;version="7.1.2"; uses:="javax.net.ssl", - org.eclipse.jgit.transport.resolver;version="7.0.2"; + org.eclipse.jgit.transport.resolver;version="7.1.2"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.lib", - org.eclipse.jgit.treewalk;version="7.0.2"; + org.eclipse.jgit.treewalk;version="7.1.2"; 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.2"; + org.eclipse.jgit.treewalk.filter;version="7.1.2"; uses:="org.eclipse.jgit.treewalk", - org.eclipse.jgit.util;version="7.0.2"; + org.eclipse.jgit.util;version="7.1.2"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.hooks, org.eclipse.jgit.revwalk, @@ -232,12 +232,12 @@ org.eclipse.jgit.treewalk, javax.net.ssl, org.eclipse.jgit.util.time", - org.eclipse.jgit.util.io;version="7.0.2"; + org.eclipse.jgit.util.io;version="7.1.2"; uses:="org.eclipse.jgit.attributes, org.eclipse.jgit.lib, org.eclipse.jgit.treewalk", - org.eclipse.jgit.util.sha1;version="7.0.2", - org.eclipse.jgit.util.time;version="7.0.2" + org.eclipse.jgit.util.sha1;version="7.1.2", + org.eclipse.jgit.util.time;version="7.1.2" Bundle-RequiredExecutionEnvironment: JavaSE-17 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)", javax.crypto,
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF index 2315007..1bf5168 100644 --- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-Name: org.eclipse.jgit - Sources Bundle-SymbolicName: org.eclipse.jgit.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 7.0.2.qualifier -Eclipse-SourceBundle: org.eclipse.jgit;version="7.0.2.qualifier";roots="." +Bundle-Version: 7.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit;version="7.1.2.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index a18b1fd..df45481 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit</artifactId>
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 fd0995a..4e2073b 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -500,7 +500,7 @@ 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. @@ -595,6 +595,8 @@ 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 @@ -638,8 +640,6 @@ 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.
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 3dc53ec..5bc035a 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 GarbageCollectCommand gc() { } /** + * 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/PackRefsCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PackRefsCommand.java new file mode 100644 index 0000000..29a69c5 --- /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/diff/RawText.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java index 76dc87e..fdfe533 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 static boolean isBinary(byte[] raw, int length, boolean complete) { 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 static boolean isCrLfText(byte[] raw, int length) { */ 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 static RawText load(ObjectLoader ldr, int threshold) 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 5de7bac..fb98df7 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/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index c309182..c8012d6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -625,6 +625,8 @@ public static JGitText get() { /***/ public String packingCancelledDuringObjectsWriting; /***/ public String packObjectCountMismatch; /***/ public String packRefs; + /***/ public String packRefsFailed; + /***/ public String packRefsSuccessful; /***/ public String packSizeNotSetYet; /***/ public String packTooLargeForIndexVersion1; /***/ public String packWasDeleted; @@ -668,8 +670,6 @@ public static JGitText get() { /***/ public String readerIsRequired; /***/ public String readingObjectsFromLocalRepositoryFailed; /***/ public String readLastModifiedFailed; - /***/ public String readPipeIsNotAllowed; - /***/ public String readPipeIsNotAllowedRequiredPermission; /***/ public String readTimedOut; /***/ public String receivePackObjectTooLarge1; /***/ public String receivePackObjectTooLarge2;
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 0000000..295b702 --- /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 ce71a71..587d482 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 @@ 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 @@ * 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 BlockCacheStats getBlockCacheStats() { - 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 3e1300c..f8e0831 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 @@ private DfsBlockCache(DfsBlockCacheConfig cfg) { 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 @@ boolean shouldCopyThroughCache(long length) { * @return total number of bytes in the cache, per pack file extension. */ public long[] getCurrentSize() { - return dfsBlockCacheTable.getBlockCacheStats().getCurrentSize(); + return getAggregatedBlockCacheStats().getCurrentSize(); } /** @@ -138,7 +146,7 @@ public long getFillPercentage() { * extension. */ public long[] getHitCount() { - return dfsBlockCacheTable.getBlockCacheStats().getHitCount(); + return getAggregatedBlockCacheStats().getHitCount(); } /** @@ -149,7 +157,7 @@ public long getFillPercentage() { * extension. */ public long[] getMissCount() { - return dfsBlockCacheTable.getBlockCacheStats().getMissCount(); + return getAggregatedBlockCacheStats().getMissCount(); } /** @@ -158,8 +166,7 @@ public long getFillPercentage() { * @return total number of requests (hit + miss), per pack file extension. */ public long[] getTotalRequestCount() { - return dfsBlockCacheTable.getBlockCacheStats() - .getTotalRequestCount(); + return getAggregatedBlockCacheStats().getTotalRequestCount(); } /** @@ -168,7 +175,7 @@ public long getFillPercentage() { * @return hit ratios */ public long[] getHitRatio() { - return dfsBlockCacheTable.getBlockCacheStats().getHitRatio(); + return getAggregatedBlockCacheStats().getHitRatio(); } /** @@ -179,7 +186,18 @@ public long getFillPercentage() { * file extension. */ public long[] getEvictions() { - return dfsBlockCacheTable.getBlockCacheStats().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 @@ <T> T get(DfsStreamKey key, long position) { 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 fa68b97..17bf518 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
@@ -19,6 +19,7 @@ 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; @@ -29,6 +30,7 @@ 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; @@ -49,6 +51,10 @@ 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; @@ -69,6 +75,7 @@ public class DfsBlockCacheConfig { * Create a default configuration. */ public DfsBlockCacheConfig() { + name = DEFAULT_NAME; setBlockLimit(32 * MB); setBlockSize(64 * KB); setStreamRatio(0.30); @@ -78,6 +85,72 @@ public DfsBlockCacheConfig() { } /** + * 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; + } + + /** * Get maximum number bytes of heap memory to dedicate to caching pack file * data. * @@ -226,12 +299,24 @@ public Map<PackExt, Integer> getCacheHotMap() { * 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. * @@ -308,6 +393,11 @@ private void fromConfig(String section, String subSection, Config rc) { 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); @@ -354,6 +444,7 @@ private void loadPackExtConfigs(Config config) { cacheConfigs.add(cacheConfig); } packExtCacheConfigurations = cacheConfigs; + setCacheHotMapToPackExtConfigs(this.cacheHotMap); } private static <T> Set<T> intersection(Set<T> first, Set<T> second) { @@ -472,6 +563,14 @@ 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, @@ -498,5 +597,11 @@ private static DfsBlockCachePackExtConfig fromConfig(Config 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())); + } } -} \ No newline at end of file +}
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 0000000..436f574 --- /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 309f2d1..c3fd07b 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,18 +126,37 @@ <T> DfsBlockCache.Ref<T> getOrLoadRef(DfsStreamKey key, long position, <T> T get(DfsStreamKey key, long position); /** - * Get the {@link BlockCacheStats} 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. * - * @return the {@link BlockCacheStats} tracking this block cache table's - * statistics. + * Use {@link AggregatedBlockCacheStats} to combine the results of the stats + * in the list for an aggregated view of the cache's stats. + * + * @return the list of {@link BlockCacheStats} held by this cache. */ - BlockCacheStats getBlockCacheStats(); + List<BlockCacheStats> getBlockCacheStats(); + + /** + * Get the name of the table. + * + * @return this table's name. + */ + String getName(); /** * Provides methods used with Block Cache statistics. */ interface BlockCacheStats { + + /** + * Get the name of the block cache generating this instance. + * + * @return this cache's name. + */ + String getName(); + /** * Get total number of bytes in the cache, per pack file extension. * @@ -190,174 +206,4 @@ interface BlockCacheStats { */ long[] getEvictions(); } - - /** - * Keeps track of stats for a Block Cache table. - */ - class DfsBlockCacheStats implements BlockCacheStats { - /** - * 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(); - } - - /** - * 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]; - } - } - } - } -} \ 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 a177669..e6068a1 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.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; @@ -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 DfsGarbageCollector setReftableInitialMinUpdateIndex(long u) { 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. * @@ -687,14 +704,7 @@ private DfsPackDescription writePack(PackSource source, PackWriter pw, 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, @@ -741,6 +751,10 @@ private void writeReftable(DfsPackDescription pack) throws IOException { 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 a07d841..16315bf 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.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.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 void checkExisting(boolean check) { 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 @@ PackIndex writePackIndex(DfsPackDescription pack, byte[] packHash, 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 616563f..efd666f 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 @@ 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.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 PackBitmapIndexWriter getPackBitmapIndexWriter( } }; } + + /** + * 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 86144b3..f9c01b9 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 @@ 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 @@ private void compactPacks(DfsReader ctx, ProgressMonitor pm) try { writePack(objdb, outDesc, pw, pm); writeIndex(objdb, outDesc, pw); + writeObjectSizeIndex(objdb, outDesc, pw); PackStatistics stats = pw.getStatistics(); @@ -458,13 +459,20 @@ private static void writePack(DfsObjDatabase objdb, 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/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java index 9cfcbaa..62f6753 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
@@ -511,18 +511,15 @@ public long getObjectSize(AnyObjectId objectId, int typeHint) 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 boolean isNotLargerThan(AnyObjectId objectId, int typeHint, } stats.isNotLargerThanCallCount += 1; - if (typeHint != Constants.OBJ_BLOB || !pack.hasObjectSizeIndex(this)) { + if (typeHint != Constants.OBJ_BLOB || !safeHasObjectSizeIndex(pack)) { return pack.getObjectSize(this, objectId) <= limit; } - long sz = pack.getIndexedObjectSize(this, objectId); + Optional<Long> maybeSz = safeGetIndexedObjectSize(pack, objectId); + if (maybeSz.isEmpty()) { + // Exception in object size index + return pack.getObjectSize(this, objectId) <= limit; + } + + 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/DfsReaderOptions.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java index 5f5e819..c397469 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,6 +13,7 @@ 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; @@ -197,6 +198,9 @@ public DfsReaderOptions fromConfig(Config rc) { 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/PackExtBlockCacheTable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTable.java index 858f731..bb44f93 100644 --- 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
@@ -37,6 +37,11 @@ * type. */ class PackExtBlockCacheTable implements DfsBlockCacheTable { + /** + * Table name. + */ + private final String name; + private final DfsBlockCacheTable defaultBlockCacheTable; // Holds the unique tables backing the extBlockCacheTables values. @@ -120,13 +125,19 @@ static PackExtBlockCacheTable fromCacheTables( Set<DfsBlockCacheTable> blockCacheTables = new HashSet<>(); blockCacheTables.add(defaultBlockCacheTable); blockCacheTables.addAll(packExtBlockCacheTables.values()); - return new PackExtBlockCacheTable(defaultBlockCacheTable, + 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(DfsBlockCacheTable defaultBlockCacheTable, + 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; @@ -177,10 +188,15 @@ public <T> T get(DfsStreamKey key, long position) { } @Override - public BlockCacheStats getBlockCacheStats() { - return new CacheStats(blockCacheTableList.stream() - .map(DfsBlockCacheTable::getBlockCacheStats) - .collect(Collectors.toList())); + 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) { @@ -196,94 +212,4 @@ private DfsBlockCacheTable getTable(DfsStreamKey key) { private static PackExt getPackExt(DfsStreamKey key) { return PackExt.values()[key.packExtPos]; } - - private static class CacheStats implements BlockCacheStats { - private final List<BlockCacheStats> blockCacheStats; - - private CacheStats(List<BlockCacheStats> blockCacheStats) { - this.blockCacheStats = blockCacheStats; - } - - @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/file/PackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackIndexWriter.java similarity index 97% rename from org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackIndexWriter.java index 87e0b44..b89cc1e 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.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 @@ * 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 static PackIndexWriter createVersion(final OutputStream dst, * 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 @@ protected PackIndexWriter(OutputStream dst) { * 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 80240e5..25b7583 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
@@ -28,8 +28,10 @@ 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; @@ -39,6 +41,7 @@ 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; @@ -108,6 +111,22 @@ public boolean hasFastTipsWithSha1() throws IOException { } /** + * {@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 */
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 b5d29a3..84c8565 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
@@ -33,6 +33,7 @@ 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 +61,6 @@ 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; @@ -595,13 +595,12 @@ private boolean shouldAutoDetach() { @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); } }
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 67b43cb..3105a3a 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.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; @@ -233,9 +235,11 @@ public GC(FileRepository repo) { * @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 +258,7 @@ public CompletableFuture<Collection<Pack>> 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 +281,8 @@ private ExecutorService executor() { 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 +291,8 @@ private Collection<Pack> doGc() throws IOException, ParseException { 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()); @@ -780,43 +786,6 @@ private static boolean equals(Ref r1, Ref r2) { } /** - * 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 @@ -1505,7 +1474,7 @@ public static class RepoStatistics { public long numberOfPackFiles; /** - * The number of pack files that were created after the last bitmap + * The number of pack files that were created since the last bitmap * generation. */ public long numberOfPackFilesSinceBitmap;
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 9f27f4b..746e124 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 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 @@ private void writeIdx() throws IOException { 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/PackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java index c0540d5..7189ce2 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
@@ -109,7 +109,7 @@ static PackIndex read(InputStream fd) throws IOException, } 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; @@ -302,8 +302,9 @@ void resolve(Set<ObjectId> matches, AbbreviatedObjectId id, * */ 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; /** @@ -321,7 +322,6 @@ public long getOffset() { * @return hex string describing the object id of this entry. */ public String name() { - ensureId(); return idBuffer.name(); } @@ -331,7 +331,6 @@ public String name() { * @return a copy of the object id. */ public ObjectId toObjectId() { - ensureId(); return idBuffer.toObjectId(); } @@ -342,32 +341,33 @@ public ObjectId toObjectId() { */ public MutableEntry cloneEntry() { final MutableEntry r = new MutableEntry(); - ensureId(); r.idBuffer.fromObjectId(idBuffer); r.offset = offset; return r; } - - void ensureId() { - // Override in implementations. - } } /** * Base implementation of the iterator over index entries. */ abstract class EntriesIterator implements Iterator<MutableEntry> { - protected final MutableEntry entry = initEntry(); - private final long objectCount; + private final MutableEntry entry = new MutableEntry(); + + /** 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; } - protected long returnedNumber = 0; - - protected abstract MutableEntry initEntry(); @Override public boolean hasNext() { @@ -379,7 +379,55 @@ public boolean hasNext() { * 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 d7c8378..be48358 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
@@ -203,7 +203,7 @@ public boolean hasCRC32Support() { @Override public Iterator<MutableEntry> iterator() { - return new IndexV1Iterator(objectCnt); + return new EntriesIteratorV1(this); } @Override @@ -246,36 +246,30 @@ private static int idOffset(int mid) { return packChecksum; } - private class IndexV1Iterator extends EntriesIterator { - int levelOne; + private static class EntriesIteratorV1 extends EntriesIterator { + private int levelOne; - int levelTwo; + private int levelTwo; - IndexV1Iterator(long objectCount) { - super(objectCount); + private final PackIndexV1 packIndex; + + private EntriesIteratorV1(PackIndexV1 packIndex) { + super(packIndex.objectCnt); + this.packIndex = packIndex; } @Override - protected MutableEntry initEntry() { - return new MutableEntry() { - @Override - protected void ensureId() { - idBuffer.fromRaw(idxdata[levelOne], levelTwo - - Constants.OBJECT_ID_LENGTH); - } - }; - } - - @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 caf8b71..36e54fc 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
@@ -224,7 +224,7 @@ public boolean hasCRC32Support() { @Override public Iterator<MutableEntry> iterator() { - return new EntriesIteratorV2(objectCnt); + return new EntriesIteratorV2(this); } @Override @@ -289,41 +289,34 @@ else if (cmp == 0) { return packChecksum; } - private class EntriesIteratorV2 extends EntriesIterator { - int levelOne; + private static class EntriesIteratorV2 extends EntriesIterator { + private int levelOne = 0; - int levelTwo; + private int levelTwo = 0; - EntriesIteratorV2(long objectCount){ - super(objectCount); + private final PackIndexV2 packIndex; + + private EntriesIteratorV2(PackIndexV2 packIndex) { + super(packIndex.objectCnt); + this.packIndex = packIndex; } @Override - protected MutableEntry initEntry() { - return new MutableEntry() { - @Override - protected void ensureId() { - idBuffer.fromRaw(names[levelOne], levelTwo - - Constants.OBJECT_ID_LENGTH / 4); - } - }; - } - - @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 7e28b5e..f0b6193 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 @@ /** * 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 fc5ef61..b72b35a 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 @@ /** * 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 1b092a3..55e047b 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.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 void flush() throws IOException { 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/PackReverseIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java index ef9753c..720a3bc 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 @@ long findNextOffset(long offset, long maxOffset) 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 cc48176..a1b7a9e 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
@@ -59,6 +59,7 @@ 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; @@ -71,6 +72,7 @@ import org.eclipse.jgit.lib.CoreConfig.TrustPackedRefsStat; 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; @@ -292,6 +294,33 @@ public void refresh() { 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.
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 01f514b..11c4547 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 @@ public void writeObjects(PackOutputStream out, List<ObjectToPack> list) * @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/pack/PackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackIndexWriter.java new file mode 100644 index 0000000..f69e68d --- /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 ca4598d..27fb814 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.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 @@ * {@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 PackWriter(Repository repo) { } /** - * 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 int getIndexVersion() { 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 int getIndexVersion() { * 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; } @@ -2498,7 +2501,7 @@ private final boolean have(ObjectToPack ptr, AnyObjectId objectId) { * 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/transport/ssh/OpenSshConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java index 3e75a9d..542d6e9 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 @@ protected List<String> validate(String key, List<String> value) { 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 HostEntry(List<String> patterns) { } 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/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java index d232be6..0c1da83 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
@@ -15,6 +15,7 @@ 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; @@ -23,7 +24,6 @@ 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 static org.eclipse.jgit.lib.Constants.GITDIR_FILE; import java.io.File; import java.io.IOException; @@ -485,7 +485,7 @@ public B readEnvironment(SystemReader sr) { 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)); } } @@ -505,7 +505,7 @@ public B readEnvironment(SystemReader sr) { 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)); } }
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 acb54d7..a57f1b7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -206,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"; @@ -1027,4 +1056,11 @@ public final class ConfigConstants { * @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"; }
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 fb5c904..76ed36a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
@@ -61,6 +61,12 @@ public String toConfigValue() { private final boolean forceAnnotated; + private final String sshDefaultKeyCommand; + + private final String sshAllowedSignersFile; + + private final String sshRevocationFile; + /** * Create a new GPG config that reads the configuration from config. * @@ -88,6 +94,17 @@ public GpgConfig(Config config) { 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); } /** @@ -151,4 +168,37 @@ public boolean isSignAllTags() { 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/PersonIdent.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java index 3ba055a..5d3db9e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
@@ -13,9 +13,11 @@ package org.eclipse.jgit.lib; import java.io.Serializable; -import java.text.SimpleDateFormat; 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; @@ -49,6 +51,17 @@ public static TimeZone getTimeZone(int tzOffset) { } /** + * Translate a minutes offset into a ZoneId + * + * @param tzOffset as minutes east of UTC + * @return a ZoneId for this offset + * @since 7.1 + */ + public static ZoneId getZoneId(int tzOffset) { + return ZoneOffset.ofHoursMinutes(tzOffset / 60, tzOffset % 60); + } + + /** * Format a timezone offset. * * @param r @@ -121,13 +134,17 @@ public static void appendSanitized(StringBuilder r, String str) { } } + // 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 +177,7 @@ public PersonIdent(PersonIdent pi) { * 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 +194,7 @@ public PersonIdent(String aName, String aEmailAddress) { */ public PersonIdent(String aName, String aEmailAddress, ProposedTimestamp when) { - this(aName, aEmailAddress, when.millis()); + this(aName, aEmailAddress, when.instant()); } /** @@ -189,8 +206,25 @@ public PersonIdent(String aName, String aEmailAddress, * 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 +236,12 @@ public PersonIdent(PersonIdent pi, Date when, TimeZone tz) { * 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 +255,7 @@ public PersonIdent(PersonIdent pi, Date aWhen) { * @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 +267,12 @@ public PersonIdent(PersonIdent pi, Instant aWhen) { * 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 +290,16 @@ public PersonIdent(final String aName, final String aEmailAddress, */ 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 +311,18 @@ public PersonIdent(final String aName, String aEmailAddress, Instant aWhen, * 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 +345,12 @@ private PersonIdent(UserConfig config) { * 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 +375,12 @@ public String getEmailAddress() { * 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 +390,19 @@ public Date getWhen() { * @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 +412,17 @@ public TimeZone getTimeZone() { * @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 +430,11 @@ public ZoneId getZoneId() { * * @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 +446,7 @@ public int getTimeZoneOffset() { public int hashCode() { int hc = getEmailAddress().hashCode(); hc *= 31; - hc += (int) (when / 1000L); + hc += when.hashCode(); return hc; } @@ -398,7 +456,9 @@ public boolean equals(Object o) { 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 +474,9 @@ public String toExternalString() { 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 +484,16 @@ public String toExternalString() { @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 2cf2418..09cb5a8 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 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 Collection<String> getConflictingNames(String name) if (existing.startsWith(prefix)) conflicting.add(existing); - return conflicting; + return Collections.unmodifiableList(conflicting); } /** @@ -593,4 +594,22 @@ public static Ref findRef(Map<String, Ref> map, String name) { } 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/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java index 1162a61..fc5ab62 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
@@ -22,6 +22,7 @@ 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 @@ protected RevCommit getBaseCommit(RevCommit a, RevCommit b, int callDepth) 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; @@ -236,4 +240,17 @@ private static PersonIdent mockAuthor(List<RevCommit> parents) { new Date((time + 1) * 1000L), TimeZone.getTimeZone("GMT+0000")); //$NON-NLS-1$ } + + 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 a50a644..dc96f65 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -1281,6 +1281,13 @@ protected boolean processEntry(CanonicalTreeParser base, 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<>( @@ -1320,8 +1327,12 @@ protected boolean processEntry(CanonicalTreeParser base, 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()) { @@ -1362,6 +1373,8 @@ protected boolean processEntry(CanonicalTreeParser base, } } } 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);
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 8373d68..863b794 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.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 void setExecutor(Executor executor) { * * @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 int getIndexVersion() { * @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/transport/SshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java index a0194ea..8120df0 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 static void setInstance(SshSessionFactory newFactory) { * @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/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java index 5ba8270..d972067 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -2384,7 +2384,8 @@ else if (ref.getName().startsWith(Constants.R_HEADS)) : 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/http/HttpConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java index 125ee6c..95b8221 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 @@ */ 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/filter/ByteArraySet.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java index bcf79a2..33db6ea 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 @@ boolean contains(byte[] toFind, int length, int hash) { } /** + * 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/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java index 860c1c9..59bbacf 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.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 static final class FileStoreAttributes { 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 @@ private static FileStoreAttributes getFileStoreAttributes(Path dir) { locks.remove(s); } return attributes; - }, FUTURE_RUNNER); + }); f = f.exceptionally(e -> { LOG.error(e.getLocalizedMessage(), e); return Optional.empty(); @@ -1391,13 +1365,6 @@ protected static String readPipe(File dir, String[] command, } } 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$
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 635351a..2378791 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 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 File resolve(File dir, String pn) { @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/GitDateParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java index 6a4b396..f080056 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 @@ * 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 0000000..7d00fcd --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitTimeParser.java
@@ -0,0 +1,213 @@ +/* + * 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.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()); + } + + // 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/Stats.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Stats.java index d957deb..efa6e7dd 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 void add(double x) { } /** - * @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 double min() { } /** - * @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 double max() { } /** - * @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 double avg() { } /** - * @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 double var() { } /** - * @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/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java index ed62c71..55cc878 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.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 long getCurrentTime() { } @Override + public Instant now() { + return Instant.now(); + } + + @Override public int getTimezone(long when) { return getTimeZone().getOffset(when) / (60 * 1000); } @@ -230,9 +237,19 @@ public long getCurrentTime() { } @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 @@ private void updateAll(Config config) * Get the current system time * * @return the current system time + * + * @deprecated Use {@link #now()} */ + @Deprecated 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 MonotonicClock getClock() { * @param when * a system timestamp * @return the local time zone + * + * @deprecated Use {@link #getTimeZoneAt(Instant)} instead. */ + @Deprecated 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 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 boolean isPerformanceTraceEnabled() { } 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/ThrowingPrintWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java index 4764676..13982b1 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 @@ 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/pom.xml b/pom.xml index ef6018b..9a0770c 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.2-SNAPSHOT</version> + <version>7.1.2-SNAPSHOT</version> <name>JGit - Parent</name> <url>${jgit-url}</url> @@ -118,8 +118,8 @@ <project.build.outputTimestamp>${commit.time.iso}</project.build.outputTimestamp> - <jgit-last-release-version>6.10.0.202406032230-r</jgit-last-release-version> - <ant-version>1.10.14</ant-version> + <jgit-last-release-version>7.0.0.202409031743-r</jgit-last-release-version> + <ant-version>1.10.15</ant-version> <apache-sshd-version>2.14.0</apache-sshd-version> <jsch-version>0.1.55</jsch-version> <jzlib-version>1.1.3</jzlib-version> @@ -130,14 +130,14 @@ <commons-compress-version>1.27.1</commons-compress-version> <osgi-core-version>6.0.0</osgi-core-version> <servlet-api-version>6.1.0</servlet-api-version> - <jetty-version>12.0.12</jetty-version> + <jetty-version>12.0.15</jetty-version> <japicmp-version>0.21.2</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.11.0</gson-version> - <bouncycastle-version>1.78.1</bouncycastle-version> + <bouncycastle-version>1.79</bouncycastle-version> <spotbugs-maven-plugin-version>4.8.5.0</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> @@ -147,8 +147,8 @@ <plexus-compiler-version>2.13.0</plexus-compiler-version> <hamcrest-version>2.2</hamcrest-version> <assertj-version>3.26.3</assertj-version> - <jna-version>5.14.0</jna-version> - <byte-buddy-version>1.15.0</byte-buddy-version> + <jna-version>5.15.0</jna-version> + <byte-buddy-version>1.15.10</byte-buddy-version> <!-- Properties to enable jacoco code coverage analysis --> <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin> @@ -911,7 +911,7 @@ <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> - <version>2.16.1</version> + <version>2.17.0</version> </dependency> <dependency> @@ -1020,7 +1020,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> - <version>5.12.0</version> + <version>5.14.2</version> </dependency> <dependency>
diff --git a/tools/BUILD b/tools/BUILD index 8c424b3..844f004 100644 --- a/tools/BUILD +++ b/tools/BUILD
@@ -10,6 +10,7 @@ java_runtime = "@rules_java//toolchains:remotejdk_17", package_configuration = [ ":error_prone", + ":error_prone_tests", ], source_version = "17", target_version = "17", @@ -22,6 +23,7 @@ java_runtime = "@rules_java//toolchains:remotejdk_21", package_configuration = [ ":error_prone", + ":error_prone_tests", ], source_version = "21", target_version = "21", @@ -32,9 +34,7 @@ # 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 @@ "-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/...", + ], +)