diff options
206 files changed, 8136 insertions, 1342 deletions
diff --git a/DEPENDENCIES b/DEPENDENCIES index 93fa850d8a..be7ab17cb4 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -2,10 +2,10 @@ maven/mavencentral/args4j/args4j/2.33, MIT, approved, CQ11068 maven/mavencentral/com.google.code.gson/gson/2.8.9, Apache-2.0, approved, CQ23496 maven/mavencentral/com.googlecode.javaewah/JavaEWAH/1.1.13, Apache-2.0, approved, CQ11658 maven/mavencentral/com.jcraft/jsch/0.1.55, BSD-3-Clause, approved, CQ19435 -maven/mavencentral/com.jcraft/jzlib/1.1.1, BSD-2-Clause, approved, CQ6218 +maven/mavencentral/com.jcraft/jzlib/1.1.3, BSD-2-Clause, approved, CQ6218 maven/mavencentral/commons-codec/commons-codec/1.11, Apache-2.0 AND BSD-3-Clause, approved, CQ15971 maven/mavencentral/commons-logging/commons-logging/1.2, Apache-2.0, approved, CQ10162 -maven/mavencentral/javax.servlet/javax.servlet-api/4.0.0, , approved, CQ16125 +maven/mavencentral/javax.servlet/javax.servlet-api/4.0.0, (CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0) AND Apache-2.0, approved, CQ16125 maven/mavencentral/junit/junit/4.13.2, EPL-2.0, approved, CQ23636 maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.9.0, Apache-2.0, approved, clearlydefined maven/mavencentral/net.bytebuddy/byte-buddy/1.9.0, Apache-2.0, approved, clearlydefined @@ -19,6 +19,8 @@ maven/mavencentral/org.apache.commons/commons-compress/1.21, Apache-2.0 AND BSD- maven/mavencentral/org.apache.commons/commons-math3/3.2, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.httpcomponents/httpclient/4.5.13, Apache-2.0 AND LicenseRef-Public-Domain, approved, CQ23527 maven/mavencentral/org.apache.httpcomponents/httpcore/4.4.14, Apache-2.0, approved, CQ23528 +maven/mavencentral/org.apache.sshd/sshd-common/2.8.0, Apache-2.0 AND ISC, approved, #2349 +maven/mavencentral/org.apache.sshd/sshd-core/2.8.0, Apache-2.0, approved, #2331 maven/mavencentral/org.apache.sshd/sshd-osgi/2.8.0, Apache-2.0, approved, CQ23892 maven/mavencentral/org.apache.sshd/sshd-sftp/2.8.0, Apache-2.0, approved, CQ23893 maven/mavencentral/org.assertj/assertj-core/3.20.2, Apache-2.0, approved, clearlydefined @@ -33,29 +35,29 @@ maven/mavencentral/org.eclipse.jetty/jetty-security/10.0.6, EPL-2.0 OR Apache-2. maven/mavencentral/org.eclipse.jetty/jetty-server/10.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-servlet/10.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-util/10.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ant.test/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ant/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.archive/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.gpg.bc/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.apache/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.server/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.test/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit.http/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit.ssh/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.server.test/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.server/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.test/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.pgm.test/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.pgm/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache.agent/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache.test/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.jsch/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.test/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ui/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit/6.1.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ant.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ant/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.archive/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.gpg.bc/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.apache/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.server/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit.http/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit.ssh/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.server.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.server/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.pgm.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.pgm/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache.agent/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.jsch/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.test/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ui/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit +maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit/6.2.0-SNAPSHOT, BSD-3-Clause, approved, technology.jgit maven/mavencentral/org.hamcrest/hamcrest-core/1.3, BSD-2-Clause, approved, CQ11429 maven/mavencentral/org.mockito/mockito-core/2.23.0, Apache-2.0 AND MIT, approved, #958 maven/mavencentral/org.objenesis/objenesis/2.6, Apache-2.0, approved, CQ15478 @@ -68,8 +68,8 @@ maven_jar( maven_jar( name = "jzlib", - artifact = "com.jcraft:jzlib:1.1.1", - sha1 = "a1551373315ffc2f96130a0e5704f74e151777ba", + artifact = "com.jcraft:jzlib:1.1.3", + sha1 = "c01428efa717624f7aabf4df319939dda9646b2d", ) maven_jar( diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF index 09975705df..e4d990cb5d 100644 --- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF @@ -5,13 +5,13 @@ Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.ant.test Bundle-SymbolicName: org.eclipse.jgit.ant.test Bundle-Vendor: %Bundle-Vendor -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.ant.tasks;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", + org.eclipse.jgit.ant.tasks;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.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 ba473eff29..724708674b 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ant.test</artifactId> diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF index 6003a31e86..066da0781f 100644 --- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF @@ -3,13 +3,13 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.ant Bundle-SymbolicName: org.eclipse.jgit.ant -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.storage.file;version="[6.1.1,6.2.0)" + org.eclipse.jgit.storage.file;version="[6.2.0,6.3.0)" Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.jgit.ant;version="6.1.1", - org.eclipse.jgit.ant.tasks;version="6.1.1"; +Export-Package: org.eclipse.jgit.ant;version="6.2.0", + org.eclipse.jgit.ant.tasks;version="6.2.0"; uses:="org.apache.tools.ant, org.apache.tools.ant.types" diff --git a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF index d0dc5c8dd1..3cde5928f2 100644 --- a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.ant - Sources Bundle-SymbolicName: org.eclipse.jgit.ant.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ant;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ant;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml index a7f258be53..75b17178ce 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-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 3fc8df1229..f5125de75c 100644 --- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.archive Bundle-SymbolicName: org.eclipse.jgit.archive -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-11 @@ -13,17 +13,17 @@ Import-Package: org.apache.commons.compress.archivers;version="[1.4,2.0)", org.apache.commons.compress.compressors.bzip2;version="[1.4,2.0)", org.apache.commons.compress.compressors.gzip;version="[1.4,2.0)", org.apache.commons.compress.compressors.xz;version="[1.4,2.0)", - org.eclipse.jgit.api;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.nls;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", + org.eclipse.jgit.api;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.nls;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.0)", org.osgi.framework;version="[1.3.0,2.0.0)" Bundle-ActivationPolicy: lazy Bundle-Activator: org.eclipse.jgit.archive.FormatActivator -Export-Package: org.eclipse.jgit.archive;version="6.1.1"; +Export-Package: org.eclipse.jgit.archive;version="6.2.0"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.api, org.apache.commons.compress.archivers, org.osgi.framework", - org.eclipse.jgit.archive.internal;version="6.1.1";x-internal:=true + org.eclipse.jgit.archive.internal;version="6.2.0";x-internal:=true diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF index 8c7fd07200..5dfc65958c 100644 --- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.archive - Sources Bundle-SymbolicName: org.eclipse.jgit.archive.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.archive;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.archive;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml index 12f3e685d8..e4f2d67120 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-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 0f72f64ff6..c8b088726d 100644 --- a/org.eclipse.jgit.benchmarks/pom.xml +++ b/org.eclipse.jgit.benchmarks/pom.xml @@ -14,7 +14,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>org.eclipse.jgit</groupId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> <artifactId>org.eclipse.jgit.benchmarks</artifactId> <packaging>jar</packaging> diff --git a/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/GetRefsBenchmark.java b/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/GetRefsBenchmark.java new file mode 100644 index 0000000000..62627e66f6 --- /dev/null +++ b/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/GetRefsBenchmark.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2021, Luca Milanesio <luca.milanesio@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.benchmarks; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; +import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; +import org.eclipse.jgit.internal.storage.file.FileRepository; +import org.eclipse.jgit.lib.*; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.ReceiveCommand; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.FileUtils; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +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 static org.eclipse.jgit.transport.ReceiveCommand.Type.CREATE; +import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE; + +@State(Scope.Thread) +public class GetRefsBenchmark { + + ThreadLocalRandom branchIndex = ThreadLocalRandom.current(); + + @State(Scope.Benchmark) + public static class BenchmarkState { + + @Param({ "true", "false" }) + boolean useRefTable; + + @Param({ "100", "2500", "10000", "50000" }) + int numBranches; + + @Param({ "true", "false" }) + boolean trustFolderStat; + + List<String> branches = new ArrayList<>(numBranches); + + Path testDir; + + Repository repo; + + @Setup + @SuppressWarnings("boxing") + public void setupBenchmark() throws IOException, GitAPIException { + String firstBranch = "firstbranch"; + testDir = Files.createDirectory(Paths.get("testrepos")); + String repoName = "branches-" + numBranches + "-trustFolderStat-" + + trustFolderStat + "-" + refDatabaseType(); + Path workDir = testDir.resolve(repoName); + Path repoPath = workDir.resolve(".git"); + Git git = Git.init().setDirectory(workDir.toFile()).call(); + RevCommit firstCommit = git.commit().setMessage("First commit") + .call(); + git.branchCreate().setName(firstBranch).call(); + + StoredConfig cfg = git.getRepository().getConfig(); + if (useRefTable) { + ((FileRepository) git.getRepository()).convertRefStorage( + ConfigConstants.CONFIG_REF_STORAGE_REFTABLE, false, + false); + } else { + cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, + trustFolderStat); + } + cfg.setInt(ConfigConstants.CONFIG_RECEIVE_SECTION, null, + "maxCommandBytes", Integer.MAX_VALUE); + cfg.save(); + + repo = RepositoryCache.open(RepositoryCache.FileKey + .lenient(repoPath.toFile(), FS.DETECTED)); + + System.out.println("Preparing test"); + System.out.println("- repository: \t\t" + repoPath); + System.out.println("- refDatabase: \t\t" + refDatabaseType()); + System.out.println("- trustFolderStat: \t" + trustFolderStat); + System.out.println("- branches: \t\t" + numBranches); + + BatchRefUpdate u = repo.getRefDatabase().newBatchUpdate(); + + branches = IntStream.range(0, numBranches) + .mapToObj(i -> "branch/" + i % 100 + "/" + i) + .collect(Collectors.toList()); + for (String branch : branches) { + u.addCommand(new ReceiveCommand(ObjectId.zeroId(), + firstCommit.toObjectId(), Constants.R_HEADS + branch, + CREATE)); + } + + System.out.println(); + System.out.print( + String.format("Creating %d branches ... ", numBranches)); + + try (RevWalk rw = new RevWalk(repo)) { + u.execute(rw, new TextProgressMonitor()); + } + System.out.println("DONE"); + } + + private String refDatabaseType() { + return useRefTable ? "reftable" : "refdir"; + } + + @TearDown + public void teardown() throws IOException { + repo.close(); + FileUtils.delete(testDir.toFile(), + FileUtils.RECURSIVE | FileUtils.RETRY); + } + } + + @Benchmark + @BenchmarkMode({ Mode.AverageTime }) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @Warmup(iterations = 2, time = 100, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = 2, time = 10, timeUnit = TimeUnit.SECONDS) + public void testGetExactRef(Blackhole blackhole, BenchmarkState state) + throws IOException { + String branchName = state.branches + .get(branchIndex.nextInt(state.numBranches)); + blackhole.consume(state.repo.exactRef(branchName)); + } + + @Benchmark + @BenchmarkMode({ Mode.AverageTime }) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @Warmup(iterations = 2, time = 100, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = 2, time = 10, timeUnit = TimeUnit.SECONDS) + public void testGetRefsByPrefix(Blackhole blackhole, BenchmarkState state) + throws IOException { + String branchPrefix = "refs/heads/branch/" + branchIndex.nextInt(100) + + "/"; + blackhole.consume( + state.repo.getRefDatabase().getRefsByPrefix(branchPrefix)); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(GetRefsBenchmark.class.getSimpleName()) + // .addProfiler(StackProfiler.class) + // .addProfiler(GCProfiler.class) + .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 abc37ec69f..a99e23e05b 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> @@ -27,88 +27,88 @@ <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ant</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.archive</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.http.apache</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.http.server</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.lfs</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.lfs.server</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.pgm</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ui</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ssh.apache</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.test</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ant.test</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.http.test</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.pgm.test</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.lfs.test</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.lfs.server.test</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> </dependencies> diff --git a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF index bb9b393f87..c92fd3e41f 100644 --- a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.gpg.bc.test Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.test -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-11 @@ -12,9 +12,9 @@ Import-Package: org.bouncycastle.jce.provider;version="[1.65.0,2.0.0)", org.bouncycastle.openpgp.operator;version="[1.65.0,2.0.0)", org.bouncycastle.openpgp.operator.jcajce;version="[1.65.0,2.0.0)", org.bouncycastle.util.encoders;version="[1.65.0,2.0.0)", - org.eclipse.jgit.gpg.bc.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.gpg.bc.internal.keys;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util.sha1;version="[6.1.1,6.2.0)", + org.eclipse.jgit.gpg.bc.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.gpg.bc.internal.keys;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util.sha1;version="[6.2.0,6.3.0)", org.hamcrest;version="[1.1.0,3.0.0)", org.junit;version="[4.13,5.0.0)", org.junit.runner;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 9568c6884d..2556889e3e 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.gpg.bc.test</artifactId> diff --git a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF index 534568dd80..fb45761616 100644 --- a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF @@ -3,10 +3,10 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.gpg.bc Bundle-SymbolicName: org.eclipse.jgit.gpg.bc;singleton:=true -Fragment-Host: org.eclipse.jgit;bundle-version="[6.1.1,6.2.0)" +Fragment-Host: org.eclipse.jgit;bundle-version="[6.2.0,6.3.0)" Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 Import-Package: org.bouncycastle.asn1;version="[1.69.0,2.0.0)", org.bouncycastle.asn1.cryptlib;version="[1.69.0,2.0.0)", @@ -29,9 +29,9 @@ Import-Package: org.bouncycastle.asn1;version="[1.69.0,2.0.0)", org.bouncycastle.util;version="[1.69.0,2.0.0)", org.bouncycastle.util.encoders;version="[1.69.0,2.0.0)", org.bouncycastle.util.io;version="[1.69.0,2.0.0)", - org.eclipse.jgit.annotations;version="[6.1.1,6.2.0)", - org.eclipse.jgit.api.errors;version="[6.1.1,6.2.0)", + org.eclipse.jgit.annotations;version="[6.2.0,6.3.0)", + org.eclipse.jgit.api.errors;version="[6.2.0,6.3.0)", org.slf4j;version="[1.7.0,2.0.0)" -Export-Package: org.eclipse.jgit.gpg.bc;version="6.1.1", - org.eclipse.jgit.gpg.bc.internal;version="6.1.1";x-friends:="org.eclipse.jgit.gpg.bc.test", - org.eclipse.jgit.gpg.bc.internal.keys;version="6.1.1";x-friends:="org.eclipse.jgit.gpg.bc.test" +Export-Package: org.eclipse.jgit.gpg.bc;version="6.2.0", + org.eclipse.jgit.gpg.bc.internal;version="6.2.0";x-friends:="org.eclipse.jgit.gpg.bc.test", + org.eclipse.jgit.gpg.bc.internal.keys;version="6.2.0";x-friends:="org.eclipse.jgit.gpg.bc.test" diff --git a/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF index 20e2d01de1..b62a2a66f2 100644 --- a/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.gpg.bc - Sources Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.gpg.bc/pom.xml b/org.eclipse.jgit.gpg.bc/pom.xml index 138f4e8a4b..69465d0917 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.gpg.bc</artifactId> diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF index afcaa78d57..df60729a87 100644 --- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.http.apache Bundle-SymbolicName: org.eclipse.jgit.http.apache -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor @@ -25,11 +25,11 @@ Import-Package: org.apache.http;version="[4.3.0,5.0.0)", org.apache.http.impl.conn;version="[4.4.0,5.0.0)", org.apache.http.params;version="[4.3.0,5.0.0)", org.apache.http.ssl;version="[4.3.0,5.0.0)", - org.eclipse.jgit.annotations;version="[6.1.1,6.2.0)", - org.eclipse.jgit.nls;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.http;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)" -Export-Package: org.eclipse.jgit.transport.http.apache;version="6.1.1"; + org.eclipse.jgit.annotations;version="[6.2.0,6.3.0)", + org.eclipse.jgit.nls;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.http;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.0)" +Export-Package: org.eclipse.jgit.transport.http.apache;version="6.2.0"; uses:="org.apache.http.client, org.eclipse.jgit.transport.http, org.apache.http.entity, diff --git a/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF index 7b8e1497eb..ead725643f 100644 --- a/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.http.apache - Sources Bundle-SymbolicName: org.eclipse.jgit.http.apache.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml index bd35589f54..0bd899f391 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-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 4bf66a2e97..cd7db79e92 100644 --- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF @@ -3,13 +3,13 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.http.server Bundle-SymbolicName: org.eclipse.jgit.http.server -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.jgit.http.server;version="6.1.1", - org.eclipse.jgit.http.server.glue;version="6.1.1"; +Export-Package: org.eclipse.jgit.http.server;version="6.2.0", + org.eclipse.jgit.http.server.glue;version="6.2.0"; uses:="javax.servlet,javax.servlet.http", - org.eclipse.jgit.http.server.resolver;version="6.1.1"; + org.eclipse.jgit.http.server.resolver;version="6.2.0"; uses:="org.eclipse.jgit.transport.resolver, org.eclipse.jgit.lib, org.eclipse.jgit.transport, @@ -18,14 +18,14 @@ Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 Import-Package: javax.servlet;version="[2.5.0,5.0.0)", javax.servlet.http;version="[2.5.0,5.0.0)", - org.eclipse.jgit.annotations;version="[6.1.1,6.2.0)", - org.eclipse.jgit.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.dfs;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.transport.parser;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.nls;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.resolver;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)" + org.eclipse.jgit.annotations;version="[6.2.0,6.3.0)", + org.eclipse.jgit.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.dfs;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.transport.parser;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.nls;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.resolver;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.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 2b47055232..b86561af0b 100644 --- a/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.http.server - Sources Bundle-SymbolicName: org.eclipse.jgit.http.server.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml index 6a788e2323..2ea4c6a344 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-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 4150bdb403..1b8caed708 100644 --- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.http.test Bundle-SymbolicName: org.eclipse.jgit.http.test -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-11 @@ -26,26 +26,26 @@ Import-Package: javax.servlet;version="[2.5.0,5.0.0)", org.eclipse.jetty.util.log;version="[10.0.0,11.0.0)", org.eclipse.jetty.util.security;version="[10.0.0,11.0.0)", org.eclipse.jetty.util.thread;version="[10.0.0,11.0.0)", - org.eclipse.jgit.api;version="[6.1.1,6.2.0)", - org.eclipse.jgit.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.http.server;version="[6.1.1,6.2.0)", - org.eclipse.jgit.http.server.glue;version="[6.1.1,6.2.0)", - org.eclipse.jgit.http.server.resolver;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.dfs;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.reftable;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit.http;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.nls;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.http;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.http.apache;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.resolver;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", + org.eclipse.jgit.api;version="[6.2.0,6.3.0)", + org.eclipse.jgit.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.http.server;version="[6.2.0,6.3.0)", + org.eclipse.jgit.http.server.glue;version="[6.2.0,6.3.0)", + org.eclipse.jgit.http.server.resolver;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.dfs;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.reftable;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit.http;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.nls;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.http;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.http.apache;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.resolver;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.0)", org.hamcrest;version="[1.1.0,3.0.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.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml index 336f43bfff..7a7b409cee 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-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 d035072b5b..50e814b404 100644 --- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.junit.http Bundle-SymbolicName: org.eclipse.jgit.junit.http -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor Bundle-ActivationPolicy: lazy @@ -21,17 +21,17 @@ Import-Package: javax.servlet;version="[2.5.0,5.0.0)", org.eclipse.jetty.util.log;version="[10.0.0,11.0.0)", org.eclipse.jetty.util.security;version="[10.0.0,11.0.0)", org.eclipse.jetty.util.ssl;version="[10.0.0,11.0.0)", - org.eclipse.jgit.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.http.server;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.resolver;version="[6.1.1,6.2.0)", + org.eclipse.jgit.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.http.server;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.resolver;version="[6.2.0,6.3.0)", org.junit;version="[4.13,5.0.0)", org.slf4j.helpers;version="[1.7.0,2.0.0)" -Export-Package: org.eclipse.jgit.junit.http;version="6.1.1"; +Export-Package: org.eclipse.jgit.junit.http;version="6.2.0"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.junit, javax.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 19142c51fd..da1714281e 100644 --- a/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.junit.http - Sources Bundle-SymbolicName: org.eclipse.jgit.junit.http.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml index d883e03879..440a06cdb4 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.junit.http</artifactId> diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF index 60858d8c01..8caf93ab8a 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-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.junit.ssh Bundle-SymbolicName: org.eclipse.jgit.junit.ssh -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor Bundle-ActivationPolicy: lazy @@ -33,16 +33,16 @@ Import-Package: org.apache.sshd.common;version="[2.8.0,2.9.0)", org.apache.sshd.server.subsystem;version="[2.8.0,2.9.0)", org.apache.sshd.sftp;version="[2.8.0,2.9.0)", org.apache.sshd.sftp.server;version="[2.8.0,2.9.0)", - org.eclipse.jgit.annotations;version="[6.1.1,6.2.0)", - org.eclipse.jgit.api;version="[6.1.1,6.2.0)", - org.eclipse.jgit.api.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", + org.eclipse.jgit.annotations;version="[6.2.0,6.3.0)", + org.eclipse.jgit.api;version="[6.2.0,6.3.0)", + org.eclipse.jgit.api.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.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,2.0.0)" -Export-Package: org.eclipse.jgit.junit.ssh;version="6.1.1" +Export-Package: org.eclipse.jgit.junit.ssh;version="6.2.0" diff --git a/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF index 41246bcee9..f6f2c2d582 100644 --- a/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.junit.ssh - Sources Bundle-SymbolicName: org.eclipse.jgit.junit.ssh.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml index 07d0218bf4..395b14b0a1 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-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 7a3b200e17..209b232303 100644 --- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF @@ -3,35 +3,35 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.junit Bundle-SymbolicName: org.eclipse.jgit.junit -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 -Import-Package: org.eclipse.jgit.annotations;version="[6.1.1,6.2.0)", - org.eclipse.jgit.api;version="[6.1.1,6.2.0)", - org.eclipse.jgit.api.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.dircache;version="[6.1.1,6.2.0)", - org.eclipse.jgit.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.pack;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.merge;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="6.1.1", - org.eclipse.jgit.treewalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.treewalk.filter;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util.io;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util.time;version="[6.1.1,6.2.0)", +Import-Package: org.eclipse.jgit.annotations;version="[6.2.0,6.3.0)", + org.eclipse.jgit.api;version="[6.2.0,6.3.0)", + org.eclipse.jgit.api.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.dircache;version="[6.2.0,6.3.0)", + org.eclipse.jgit.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.pack;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.merge;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="6.2.0", + org.eclipse.jgit.treewalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.treewalk.filter;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util.io;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util.time;version="[6.2.0,6.3.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,2.0.0)" -Export-Package: org.eclipse.jgit.junit;version="6.1.1"; +Export-Package: org.eclipse.jgit.junit;version="6.2.0"; uses:="org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, @@ -44,4 +44,4 @@ Export-Package: org.eclipse.jgit.junit;version="6.1.1"; org.junit.runners.model, org.junit.runner, org.eclipse.jgit.util.time", - org.eclipse.jgit.junit.time;version="6.1.1";uses:="org.eclipse.jgit.util.time" + org.eclipse.jgit.junit.time;version="6.2.0";uses:="org.eclipse.jgit.util.time" diff --git a/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF index 25876c7fc7..f1dad6a839 100644 --- a/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.junit - Sources Bundle-SymbolicName: org.eclipse.jgit.junit.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.junit;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.junit;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml index 2c260e2475..13bb6a7458 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.junit</artifactId> 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 cbb8135d77..23078bdc83 100644 --- a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.lfs.server.test Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-11 @@ -26,24 +26,24 @@ Import-Package: javax.servlet;version="[3.1.0,5.0.0)", org.eclipse.jetty.util.log;version="[10.0.0,11.0.0)", org.eclipse.jetty.util.security;version="[10.0.0,11.0.0)", org.eclipse.jetty.util.thread;version="[10.0.0,11.0.0)", - org.eclipse.jgit.api;version="[6.1.1,6.2.0)", - org.eclipse.jgit.api.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit.http;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.server;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.server.fs;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.test;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.treewalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.treewalk.filter;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", + org.eclipse.jgit.api;version="[6.2.0,6.3.0)", + org.eclipse.jgit.api.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit.http;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.server;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.server.fs;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.test;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.treewalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.treewalk.filter;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.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 1bab620881..abc559459f 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs.server.test</artifactId> diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF index cd8bde3a8e..067559f108 100644 --- a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF @@ -3,19 +3,19 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.lfs.server Bundle-SymbolicName: org.eclipse.jgit.lfs.server -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.jgit.lfs.server;version="6.1.1"; +Export-Package: org.eclipse.jgit.lfs.server;version="6.2.0"; uses:="javax.servlet.http, org.eclipse.jgit.lfs.lib", - org.eclipse.jgit.lfs.server.fs;version="6.1.1"; + org.eclipse.jgit.lfs.server.fs;version="6.2.0"; uses:="javax.servlet, javax.servlet.http, org.eclipse.jgit.lfs.server, org.eclipse.jgit.lfs.lib", - org.eclipse.jgit.lfs.server.internal;version="6.1.1";x-internal:=true, - org.eclipse.jgit.lfs.server.s3;version="6.1.1"; + org.eclipse.jgit.lfs.server.internal;version="6.2.0";x-internal:=true, + org.eclipse.jgit.lfs.server.s3;version="6.2.0"; uses:="org.eclipse.jgit.lfs.server, org.eclipse.jgit.lfs.lib" Bundle-RequiredExecutionEnvironment: JavaSE-11 @@ -24,15 +24,15 @@ Import-Package: com.google.gson;version="[2.8.0,3.0.0)", javax.servlet.annotation;version="[3.1.0,5.0.0)", javax.servlet.http;version="[3.1.0,5.0.0)", org.apache.http;version="[4.3.0,5.0.0)", - org.eclipse.jgit.annotations;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.nls;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.http;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.http.apache;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", + org.eclipse.jgit.annotations;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.nls;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.http;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.http.apache;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.0)", org.slf4j;version="[1.7.0,2.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 5a5b23add3..a640bf39ff 100644 --- a/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.lfs.server - Sources Bundle-SymbolicName: org.eclipse.jgit.lfs.server.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml index ae27153d71..354ed59a79 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs.server</artifactId> diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF index e3c0af81de..19f852b72b 100644 --- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF @@ -3,27 +3,27 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.lfs.test Bundle-SymbolicName: org.eclipse.jgit.lfs.test -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-11 -Import-Package: org.eclipse.jgit.api;version="[6.1.1,6.2.0)", - org.eclipse.jgit.attributes;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.dfs;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.http;version="[6.1.1,6.2.0)", - org.eclipse.jgit.treewalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.treewalk.filter;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", +Import-Package: org.eclipse.jgit.api;version="[6.2.0,6.3.0)", + org.eclipse.jgit.attributes;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.dfs;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.http;version="[6.2.0,6.3.0)", + org.eclipse.jgit.treewalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.treewalk.filter;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.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="6.1.1";x-friends:="org.eclipse.jgit.lfs.server.test" +Export-Package: org.eclipse.jgit.lfs.test;version="6.2.0";x-friends:="org.eclipse.jgit.lfs.server.test" diff --git a/org.eclipse.jgit.lfs.test/pom.xml b/org.eclipse.jgit.lfs.test/pom.xml index 1d782e6fd3..5b401ad755 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs.test</artifactId> diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/LfsConfigGitTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/LfsConfigGitTest.java index 98a0712e47..3ac41571a4 100644 --- a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/LfsConfigGitTest.java +++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/LfsConfigGitTest.java @@ -142,14 +142,6 @@ public class LfsConfigGitTest extends RepositoryTestCase { File gitAttributesFile; private void createLfsFiles(String lfsPointer) throws Exception { - /* - * FileNames ".aaa.txt" and "zzz.txt" seem to be sufficient to get the - * desired checkout order before and after ".lfsconfig", at least in a - * number of manual tries. Since the files to checkout are contained in - * a set (see DirCacheCheckout::doCheckout) the order cannot be - * guaranteed. - */ - //File to be checked out before lfs config String fileNameBefore = ".aaa.txt"; fileBefore = writeTrashFile(fileNameBefore, lfsPointer); diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF index 09ffffbf86..7a9b5b9e40 100644 --- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF @@ -3,33 +3,32 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.lfs Bundle-SymbolicName: org.eclipse.jgit.lfs -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.jgit.lfs;version="6.1.1", - org.eclipse.jgit.lfs.errors;version="6.1.1", - org.eclipse.jgit.lfs.internal;version="6.1.1";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server", - org.eclipse.jgit.lfs.lib;version="6.1.1" +Export-Package: org.eclipse.jgit.lfs;version="6.2.0", + org.eclipse.jgit.lfs.errors;version="6.2.0", + org.eclipse.jgit.lfs.internal;version="6.2.0";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server", + org.eclipse.jgit.lfs.lib;version="6.2.0" Bundle-RequiredExecutionEnvironment: JavaSE-11 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="[6.1.1,6.2.0)";resolution:=optional, - org.eclipse.jgit.api.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.attributes;version="[6.1.1,6.2.0)", - org.eclipse.jgit.diff;version="[6.1.1,6.2.0)", - org.eclipse.jgit.dircache;version="[6.1.1,6.2.0)", - org.eclipse.jgit.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.hooks;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.nls;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.storage.pack;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.http;version="[6.1.1,6.2.0)", - org.eclipse.jgit.treewalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.treewalk.filter;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util.io;version="[6.1.1,6.2.0)", - org.slf4j;version="[1.7.0,2.0.0)" + org.eclipse.jgit.annotations;version="[6.2.0,6.3.0)";resolution:=optional, + org.eclipse.jgit.api.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.attributes;version="[6.2.0,6.3.0)", + org.eclipse.jgit.diff;version="[6.2.0,6.3.0)", + org.eclipse.jgit.dircache;version="[6.2.0,6.3.0)", + org.eclipse.jgit.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.hooks;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.nls;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.storage.pack;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.http;version="[6.2.0,6.3.0)", + org.eclipse.jgit.treewalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.treewalk.filter;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util.io;version="[6.2.0,6.3.0)" diff --git a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF index 7a2a446c48..2eef821118 100644 --- a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.lfs - Sources Bundle-SymbolicName: org.eclipse.jgit.lfs.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml index 949c568217..4d2766ee27 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs</artifactId> diff --git a/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties b/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties index 642b83db44..c4c0dacf42 100644 --- a/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties +++ b/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties @@ -6,14 +6,13 @@ incorrectLONG_OBJECT_ID_LENGTH=Incorrect LONG_OBJECT_ID_LENGTH. invalidLongId=Invalid id: {0} invalidLongIdLength=Invalid id length {0}; should be {1} lfsFailedToGetRepository=failed to get repository {0} -lfsNoDownloadUrl="Need to download object from LFS server but couldn't determine LFS server URL" +lfsNoDownloadUrl=Need to download object from LFS server but couldn't determine LFS server URL lfsUnauthorized=Not authorized to perform operation {0} on repository {1} lfsUnavailable=LFS is not available for repository {0} -missingLocalObject="Local Object {0} is missing" +missingLocalObject=Local Object {0} is missing protocolError=LFS Protocol Error {0}: {1} repositoryNotFound=Repository {0} not found repositoryReadOnly=Repository {0} is read-only requiredHashFunctionNotAvailable=Required hash function {0} not available. serverFailure=When trying to open a connection to {0} the server responded with an error code. rc={1} -userConfigInvalid="User config file {0} invalid {1}" wrongAmountOfDataReceived=While downloading data from the content server {0} {1} bytes have been received while {2} have been expected
\ No newline at end of file diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java index ebf46e080e..9b3d60812a 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017, Markus Duft <markus.duft@ssi-schaefer.com> and others + * Copyright (C) 2017, 2022 Markus Duft <markus.duft@ssi-schaefer.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 @@ -101,8 +101,10 @@ public class LfsPrePushHook extends PrePushHook { } HttpConnection api = LfsConnectionFactory.getLfsConnection( getRepository(), METHOD_POST, OPERATION_UPLOAD); - Map<String, LfsPointer> oid2ptr = requestBatchUpload(api, toPush); - uploadContents(api, oid2ptr); + if (!isDryRun()) { + Map<String, LfsPointer> oid2ptr = requestBatchUpload(api, toPush); + uploadContents(api, oid2ptr); + } return EMPTY; } diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConfig.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConfig.java index 71d395ca84..857ccbe056 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConfig.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConfig.java @@ -30,21 +30,26 @@ import org.eclipse.jgit.treewalk.TreeWalk; import static org.eclipse.jgit.lib.Constants.HEAD; /** - * Encapsulate access to the .lfsconfig. + * Encapsulate access to the {@code .lfsconfig}. + * <p> + * According to the git lfs documentation the order to find the + * {@code .lfsconfig} file is: + * </p> + * <ol> + * <li>in the root of the working tree</li> + * <li>in the index</li> + * <li>in the HEAD; for bare repositories this is the only place that is + * searched</li> + * </ol> + * <p> + * Values from the {@code .lfsconfig} are used only if not specified in another + * git config file to allow local override without modifiction of a committed + * file. + * </p> * - * According to the document - * https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-config.5.ronn - * the order to find the .lfsconfig file is: - * - * <pre> - * 1. in the root of the working tree - * 2. in the index - * 3. in the HEAD, for bare repositories this is the only place - * that is searched - * </pre> - * - * Values from the .lfsconfig are used only if not specified in another git - * config file to allow local override without modifiction of a committed file. + * @see <a href= + * "https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-config.5.ronn">Configuration + * options for git-lfs</a> */ public class LfsConfig { private Repository db; @@ -55,17 +60,30 @@ public class LfsConfig { * * @param db * the associated repo - * @throws IOException */ - public LfsConfig(Repository db) throws IOException { + public LfsConfig(Repository db) { this.db = db; - delegate = this.load(); + } + + /** + * Getter for the delegate to allow lazy initialization. + * + * @return the delegate {@link Config} + * @throws IOException + */ + private Config getDelegate() throws IOException { + if (delegate == null) { + delegate = this.load(); + } + return delegate; } /** * Read the .lfsconfig file from the repository * - * @return The loaded lfs config or null if it does not exist + * An empty config is returned be empty if no lfs config exists. + * + * @return The loaded lfs config * * @throws IOException */ @@ -102,7 +120,7 @@ public class LfsConfig { throws IOException { File lfsConfig = db.getFS().resolve(db.getWorkTree(), Constants.DOT_LFS_CONFIG); - if (lfsConfig.exists() && lfsConfig.isFile()) { + if (lfsConfig.isFile()) { FileBasedConfig config = new FileBasedConfig(lfsConfig, db.getFS()); try { config.load(); @@ -188,12 +206,14 @@ public class LfsConfig { * @param name * the key name * @return a String value from the config, <code>null</code> if not found + * @throws IOException */ + @Nullable public String getString(final String section, final String subsection, - final String name) { + final String name) throws IOException { String result = db.getConfig().getString(section, subsection, name); if (result == null) { - result = delegate.getString(section, subsection, name); + result = getDelegate().getString(section, subsection, name); } return result; } diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java index 06234c1d90..8ef8f59f93 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java @@ -44,6 +44,5 @@ public class LfsText extends TranslationBundle { /***/ public String repositoryReadOnly; /***/ public String requiredHashFunctionNotAvailable; /***/ public String serverFailure; - /***/ public String userConfigInvalid; /***/ public String wrongAmountOfDataReceived; } diff --git a/org.eclipse.jgit.packaging/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..99f26c0203 --- /dev/null +++ b/org.eclipse.jgit.packaging/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/<project>=UTF-8 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 cd22e24ba8..2d52eeddef 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="6.1.1.qualifier" + version="6.2.0.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml index 54feb9d384..7d591ed253 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..99f26c0203 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/<project>=UTF-8 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 2061948432..cd6746bb0f 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="6.1.1.qualifier" + version="6.2.0.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -23,7 +23,7 @@ </url> <requires> - <import plugin="org.eclipse.jgit" version="6.1.1" match="equivalent"/> + <import plugin="org.eclipse.jgit" version="6.2.0" match="equivalent"/> </requires> <plugin diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml index 760903e7e0..17ead4e244 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml index e899ad56d3..30812016fe 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="6.1.1.qualifier" + version="6.2.0.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -23,7 +23,7 @@ </url> <requires> - <import plugin="org.eclipse.jgit" version="6.1.1" match="equivalent"/> + <import plugin="org.eclipse.jgit" version="6.2.0" match="equivalent"/> </requires> <plugin diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml index b725dff216..cf091c30d8 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml index 99073e4710..9d5839ebb6 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="6.1.1.qualifier" + version="6.2.0.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -24,7 +24,7 @@ <requires> <import plugin="com.jcraft.jsch"/> - <import plugin="org.eclipse.jgit" version="6.1.1" match="equivalent"/> + <import plugin="org.eclipse.jgit" version="6.2.0" match="equivalent"/> </requires> <plugin diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml index 3566dbb4a0..938edc8ed5 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml index c04051b604..23159ecb6e 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="6.1.1.qualifier" + version="6.2.0.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -23,7 +23,7 @@ </url> <requires> - <import feature="org.eclipse.jgit" version="6.1.1" match="equivalent"/> + <import feature="org.eclipse.jgit" version="6.2.0" match="equivalent"/> </requires> <plugin diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml index 50338a60c5..b416432880 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml index 3939f51b9b..985233bfdb 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="6.1.1.qualifier" + version="6.2.0.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -35,9 +35,9 @@ version="0.0.0"/> <requires> - <import feature="org.eclipse.jgit" version="6.1.1" match="equivalent"/> - <import feature="org.eclipse.jgit.lfs" version="6.1.1" match="equivalent"/> - <import feature="org.eclipse.jgit.ssh.apache" version="6.1.1" match="equivalent"/> + <import feature="org.eclipse.jgit" version="6.2.0" match="equivalent"/> + <import feature="org.eclipse.jgit.lfs" version="6.2.0" match="equivalent"/> + <import feature="org.eclipse.jgit.ssh.apache" version="6.2.0" match="equivalent"/> </requires> <plugin diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml index 4b0e26aa94..bf8e938b89 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-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 9ccf39eafa..b38d1135d3 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.repository</artifactId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml index d3e662b9ba..f64898d2c8 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="6.1.1.qualifier" + version="6.2.0.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -23,7 +23,7 @@ </url> <requires> - <import feature="org.eclipse.jgit" version="6.1.1" match="equivalent"/> + <import feature="org.eclipse.jgit" version="6.2.0" match="equivalent"/> </requires> <plugin diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml index d8b5de2436..fe364838db 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> @@ -30,7 +30,7 @@ <dependency> <groupId>org.eclipse.jgit.feature</groupId> <artifactId>org.eclipse.jgit</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </dependency> </dependencies> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml index e03964727e..503ce011a9 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="6.1.1.qualifier" + version="6.2.0.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -23,7 +23,7 @@ </url> <requires> - <import feature="org.eclipse.jgit" version="6.1.1" match="equivalent"/> + <import feature="org.eclipse.jgit" version="6.2.0" match="equivalent"/> </requires> <plugin diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml index 591b448016..ab1f5d37d3 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..99f26c0203 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/<project>=UTF-8 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 3df4a9284a..94485a285e 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="6.1.1.qualifier" + version="6.2.0.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -23,7 +23,7 @@ </url> <requires> - <import plugin="org.eclipse.jgit" version="6.1.1" match="equivalent"/> + <import plugin="org.eclipse.jgit" version="6.2.0" match="equivalent"/> </requires> <plugin diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml index e461bff45a..a93c70d69e 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..99f26c0203 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/<project>=UTF-8 diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF index 5d61eefb64..a72e3851a2 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF @@ -2,4 +2,4 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: JGit Target Platform Bundle Bundle-SymbolicName: org.eclipse.jgit.target -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target index 4e4023232c..3d4f2378d1 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target @@ -1,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.17" sequenceNumber="1646256653"> +<target name="jgit-4.17" sequenceNumber="1654550635"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="jakarta.servlet-api" version="4.0.0"/> @@ -27,8 +27,8 @@ <unit id="com.google.gson.source" version="2.8.9.v20220111-1409"/> <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/> <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/> - <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> - <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> + <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/> + <unit id="com.jcraft.jzlib.source" version="1.1.3.v20220502-1820"/> <unit id="com.sun.jna" version="5.8.0.v20210503-0343"/> <unit id="com.sun.jna.source" version="5.8.0.v20210503-0343"/> <unit id="com.sun.jna.platform" version="5.8.0.v20210406-1004"/> @@ -39,8 +39,8 @@ <unit id="net.bytebuddy.byte-buddy-agent" version="1.9.0.v20181106-1534"/> <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.9.0.v20181106-1534"/> <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/> - <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20210923-1401"/> - <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20210923-1401"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20220506-1020"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20220506-1020"/> <unit id="org.apache.ant" version="1.10.12.v20211102-1452"/> <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/> <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/> @@ -59,12 +59,12 @@ <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/> <unit id="org.assertj" version="3.20.2.v20210706-1104"/> <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/> - <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/> <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/> <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/> <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/> <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/> <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/> @@ -87,7 +87,7 @@ <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/> <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/> <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/> - <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220302172233/repository"/> + <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd index dbb450a1dc..9c824c5fd4 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd @@ -1,7 +1,7 @@ target "jgit-4.17" with source configurePhase include "projects/jetty-10.0.x.tpd" -include "orbit/R20220302172233-2022-03.tpd" +include "orbit/R20220531185310-2022-06.tpd" location "https://download.eclipse.org/releases/2020-09/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target index 1628217e04..603fefbadf 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target @@ -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.18" sequenceNumber="1646256653"> +<target name="jgit-4.18" sequenceNumber="1654550635"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="jakarta.servlet-api" version="4.0.0"/> @@ -27,8 +27,8 @@ <unit id="com.google.gson.source" version="2.8.9.v20220111-1409"/> <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/> <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/> - <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> - <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> + <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/> + <unit id="com.jcraft.jzlib.source" version="1.1.3.v20220502-1820"/> <unit id="com.sun.jna" version="5.8.0.v20210503-0343"/> <unit id="com.sun.jna.source" version="5.8.0.v20210503-0343"/> <unit id="com.sun.jna.platform" version="5.8.0.v20210406-1004"/> @@ -39,8 +39,8 @@ <unit id="net.bytebuddy.byte-buddy-agent" version="1.9.0.v20181106-1534"/> <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.9.0.v20181106-1534"/> <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/> - <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20210923-1401"/> - <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20210923-1401"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20220506-1020"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20220506-1020"/> <unit id="org.apache.ant" version="1.10.12.v20211102-1452"/> <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/> <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/> @@ -59,12 +59,12 @@ <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/> <unit id="org.assertj" version="3.20.2.v20210706-1104"/> <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/> - <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/> <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/> <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/> <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/> <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/> <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/> @@ -87,7 +87,7 @@ <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/> <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/> <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/> - <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220302172233/repository"/> + <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd index 911c67c908..1dcdd9bebb 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd @@ -1,7 +1,7 @@ target "jgit-4.18" with source configurePhase include "projects/jetty-10.0.x.tpd" -include "orbit/R20220302172233-2022-03.tpd" +include "orbit/R20220531185310-2022-06.tpd" location "https://download.eclipse.org/releases/2020-12/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target index ab18f7b01c..cc418b1c8a 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target @@ -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.19-staging" sequenceNumber="1646256653"> +<target name="jgit-4.19-staging" sequenceNumber="1654550632"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="jakarta.servlet-api" version="4.0.0"/> @@ -27,8 +27,8 @@ <unit id="com.google.gson.source" version="2.8.9.v20220111-1409"/> <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/> <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/> - <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> - <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> + <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/> + <unit id="com.jcraft.jzlib.source" version="1.1.3.v20220502-1820"/> <unit id="com.sun.jna" version="5.8.0.v20210503-0343"/> <unit id="com.sun.jna.source" version="5.8.0.v20210503-0343"/> <unit id="com.sun.jna.platform" version="5.8.0.v20210406-1004"/> @@ -39,8 +39,8 @@ <unit id="net.bytebuddy.byte-buddy-agent" version="1.9.0.v20181106-1534"/> <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.9.0.v20181106-1534"/> <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/> - <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20210923-1401"/> - <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20210923-1401"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20220506-1020"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20220506-1020"/> <unit id="org.apache.ant" version="1.10.12.v20211102-1452"/> <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/> <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/> @@ -59,12 +59,12 @@ <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/> <unit id="org.assertj" version="3.20.2.v20210706-1104"/> <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/> - <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/> <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/> <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/> <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/> <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/> <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/> @@ -87,7 +87,7 @@ <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/> <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/> <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/> - <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220302172233/repository"/> + <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd index fdb8b11f6f..806851c6e2 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd @@ -1,7 +1,7 @@ target "jgit-4.19-staging" with source configurePhase include "projects/jetty-10.0.x.tpd" -include "orbit/R20220302172233-2022-03.tpd" +include "orbit/R20220531185310-2022-06.tpd" location "https://download.eclipse.org/staging/2021-03/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target index 4c840dc34a..efcb591efa 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target @@ -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.20" sequenceNumber="1646256653"> +<target name="jgit-4.20" sequenceNumber="1654550634"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="jakarta.servlet-api" version="4.0.0"/> @@ -27,8 +27,8 @@ <unit id="com.google.gson.source" version="2.8.9.v20220111-1409"/> <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/> <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/> - <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> - <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> + <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/> + <unit id="com.jcraft.jzlib.source" version="1.1.3.v20220502-1820"/> <unit id="com.sun.jna" version="5.8.0.v20210503-0343"/> <unit id="com.sun.jna.source" version="5.8.0.v20210503-0343"/> <unit id="com.sun.jna.platform" version="5.8.0.v20210406-1004"/> @@ -39,8 +39,8 @@ <unit id="net.bytebuddy.byte-buddy-agent" version="1.9.0.v20181106-1534"/> <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.9.0.v20181106-1534"/> <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/> - <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20210923-1401"/> - <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20210923-1401"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20220506-1020"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20220506-1020"/> <unit id="org.apache.ant" version="1.10.12.v20211102-1452"/> <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/> <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/> @@ -59,12 +59,12 @@ <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/> <unit id="org.assertj" version="3.20.2.v20210706-1104"/> <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/> - <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/> <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/> <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/> <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/> <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/> <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/> @@ -87,7 +87,7 @@ <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/> <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/> <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/> - <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220302172233/repository"/> + <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd index 120ee647b0..a3ea58300e 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd @@ -1,7 +1,7 @@ target "jgit-4.20" with source configurePhase include "projects/jetty-10.0.x.tpd" -include "orbit/R20220302172233-2022-03.tpd" +include "orbit/R20220531185310-2022-06.tpd" location "https://download.eclipse.org/releases/2021-06/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target index 7e8cd91fe3..85ed31f441 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target @@ -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.21" sequenceNumber="1646256653"> +<target name="jgit-4.21" sequenceNumber="1654550635"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="jakarta.servlet-api" version="4.0.0"/> @@ -27,8 +27,8 @@ <unit id="com.google.gson.source" version="2.8.9.v20220111-1409"/> <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/> <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/> - <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> - <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> + <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/> + <unit id="com.jcraft.jzlib.source" version="1.1.3.v20220502-1820"/> <unit id="com.sun.jna" version="5.8.0.v20210503-0343"/> <unit id="com.sun.jna.source" version="5.8.0.v20210503-0343"/> <unit id="com.sun.jna.platform" version="5.8.0.v20210406-1004"/> @@ -39,8 +39,8 @@ <unit id="net.bytebuddy.byte-buddy-agent" version="1.9.0.v20181106-1534"/> <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.9.0.v20181106-1534"/> <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/> - <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20210923-1401"/> - <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20210923-1401"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20220506-1020"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20220506-1020"/> <unit id="org.apache.ant" version="1.10.12.v20211102-1452"/> <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/> <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/> @@ -59,12 +59,12 @@ <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/> <unit id="org.assertj" version="3.20.2.v20210706-1104"/> <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/> - <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/> <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/> <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/> <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/> <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/> <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/> @@ -87,7 +87,7 @@ <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/> <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/> <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/> - <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220302172233/repository"/> + <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd index 0ec2a52a2c..0808601826 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd @@ -1,7 +1,7 @@ target "jgit-4.21" with source configurePhase include "projects/jetty-10.0.x.tpd" -include "orbit/R20220302172233-2022-03.tpd" +include "orbit/R20220531185310-2022-06.tpd" location "https://download.eclipse.org/releases/2021-09/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target index b229da160b..e7b5a31992 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target @@ -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.22" sequenceNumber="1646256653"> +<target name="jgit-4.22" sequenceNumber="1654550634"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="jakarta.servlet-api" version="4.0.0"/> @@ -27,8 +27,8 @@ <unit id="com.google.gson.source" version="2.8.9.v20220111-1409"/> <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/> <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/> - <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> - <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> + <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/> + <unit id="com.jcraft.jzlib.source" version="1.1.3.v20220502-1820"/> <unit id="com.sun.jna" version="5.8.0.v20210503-0343"/> <unit id="com.sun.jna.source" version="5.8.0.v20210503-0343"/> <unit id="com.sun.jna.platform" version="5.8.0.v20210406-1004"/> @@ -39,8 +39,8 @@ <unit id="net.bytebuddy.byte-buddy-agent" version="1.9.0.v20181106-1534"/> <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.9.0.v20181106-1534"/> <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/> - <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20210923-1401"/> - <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20210923-1401"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20220506-1020"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20220506-1020"/> <unit id="org.apache.ant" version="1.10.12.v20211102-1452"/> <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/> <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/> @@ -59,12 +59,12 @@ <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/> <unit id="org.assertj" version="3.20.2.v20210706-1104"/> <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/> - <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/> <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/> <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220105-1522"/> - <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/> <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/> <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/> <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/> @@ -87,7 +87,7 @@ <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/> <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/> <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/> - <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220302172233/repository"/> + <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd index eb1723c736..5697574d3f 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd @@ -1,7 +1,7 @@ target "jgit-4.22" with source configurePhase include "projects/jetty-10.0.x.tpd" -include "orbit/R20220302172233-2022-03.tpd" +include "orbit/R20220531185310-2022-06.tpd" location "https://download.eclipse.org/releases/2021-12/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target new file mode 100644 index 0000000000..3c18e4b75c --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<?pde?> +<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> +<target name="jgit-4.23" sequenceNumber="1654550634"> + <locations> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="jakarta.servlet-api" version="4.0.0"/> + <unit id="jakarta.servlet-api.source" version="4.0.0"/> + <unit id="org.eclipse.jetty.http" version="10.0.6"/> + <unit id="org.eclipse.jetty.http.source" version="10.0.6"/> + <unit id="org.eclipse.jetty.io" version="10.0.6"/> + <unit id="org.eclipse.jetty.io.source" version="10.0.6"/> + <unit id="org.eclipse.jetty.security" version="10.0.6"/> + <unit id="org.eclipse.jetty.security.source" version="10.0.6"/> + <unit id="org.eclipse.jetty.server" version="10.0.6"/> + <unit id="org.eclipse.jetty.server.source" version="10.0.6"/> + <unit id="org.eclipse.jetty.servlet" version="10.0.6"/> + <unit id="org.eclipse.jetty.servlet.source" version="10.0.6"/> + <unit id="org.eclipse.jetty.util" version="10.0.6"/> + <unit id="org.eclipse.jetty.util.source" version="10.0.6"/> + <unit id="org.eclipse.jetty.util.ajax" version="10.0.6"/> + <unit id="org.eclipse.jetty.util.ajax.source" version="10.0.6"/> + <repository id="jetty-10.0.x" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/> + </location> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="com.google.gson" version="2.8.9.v20220111-1409"/> + <unit id="com.google.gson.source" version="2.8.9.v20220111-1409"/> + <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/> + <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/> + <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/> + <unit id="com.jcraft.jzlib.source" version="1.1.3.v20220502-1820"/> + <unit id="com.sun.jna" version="5.8.0.v20210503-0343"/> + <unit id="com.sun.jna.source" version="5.8.0.v20210503-0343"/> + <unit id="com.sun.jna.platform" version="5.8.0.v20210406-1004"/> + <unit id="com.sun.jna.platform.source" version="5.8.0.v20210406-1004"/> + <unit id="javaewah" version="1.1.13.v20211029-0839"/> + <unit id="javaewah.source" version="1.1.13.v20211029-0839"/> + <unit id="net.bytebuddy.byte-buddy" version="1.9.0.v20181107-1410"/> + <unit id="net.bytebuddy.byte-buddy-agent" version="1.9.0.v20181106-1534"/> + <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.9.0.v20181106-1534"/> + <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20220506-1020"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20220506-1020"/> + <unit id="org.apache.ant" version="1.10.12.v20211102-1452"/> + <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/> + <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/> + <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/> + <unit id="org.apache.commons.compress" version="1.21.0.v20211103-2100"/> + <unit id="org.apache.commons.compress.source" version="1.21.0.v20211103-2100"/> + <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/> + <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/> + <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/> + <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/> + <unit id="org.apache.httpcomponents.httpcore" version="4.4.15.v20220209-2345"/> + <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.15.v20220209-2345"/> + <unit id="org.apache.sshd.osgi" version="2.8.0.v20211227-1750"/> + <unit id="org.apache.sshd.osgi.source" version="2.8.0.v20211227-1750"/> + <unit id="org.apache.sshd.sftp" version="2.8.0.v20211227-1750"/> + <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/> + <unit id="org.assertj" version="3.20.2.v20210706-1104"/> + <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/> + <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/> + <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/> + <unit id="org.hamcrest.source" version="2.2.0.v20210711-0821"/> + <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/> + <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/> + <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/> + <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/> + <unit id="org.junit" version="4.13.2.v20211018-1956"/> + <unit id="org.junit.source" version="4.13.2.v20211018-1956"/> + <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> + <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> + <unit id="org.mockito" version="2.23.0.v20200310-1642"/> + <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/> + <unit id="org.objenesis" version="2.6.0.v20180420-1519"/> + <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/> + <unit id="org.slf4j.api" version="1.7.30.v20200204-2150"/> + <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/> + <unit id="org.slf4j.binding.simple" version="1.7.30.v20200204-2150"/> + <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/> + <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/> + <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/> + <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/> + </location> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="org.eclipse.osgi" version="0.0.0"/> + <repository location="https://download.eclipse.org/releases/2022-03/"/> + </location> + </locations> +</target> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd new file mode 100644 index 0000000000..7fd421a882 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd @@ -0,0 +1,8 @@ +target "jgit-4.23" with source configurePhase + +include "projects/jetty-10.0.x.tpd" +include "orbit/R20220531185310-2022-06.tpd" + +location "https://download.eclipse.org/releases/2022-03/" { + org.eclipse.osgi lazy +} diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target new file mode 100644 index 0000000000..b622b4e91e --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<?pde?> +<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> +<target name="jgit-4.24" sequenceNumber="1654550621"> + <locations> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="jakarta.servlet-api" version="4.0.0"/> + <unit id="jakarta.servlet-api.source" version="4.0.0"/> + <unit id="org.eclipse.jetty.http" version="10.0.6"/> + <unit id="org.eclipse.jetty.http.source" version="10.0.6"/> + <unit id="org.eclipse.jetty.io" version="10.0.6"/> + <unit id="org.eclipse.jetty.io.source" version="10.0.6"/> + <unit id="org.eclipse.jetty.security" version="10.0.6"/> + <unit id="org.eclipse.jetty.security.source" version="10.0.6"/> + <unit id="org.eclipse.jetty.server" version="10.0.6"/> + <unit id="org.eclipse.jetty.server.source" version="10.0.6"/> + <unit id="org.eclipse.jetty.servlet" version="10.0.6"/> + <unit id="org.eclipse.jetty.servlet.source" version="10.0.6"/> + <unit id="org.eclipse.jetty.util" version="10.0.6"/> + <unit id="org.eclipse.jetty.util.source" version="10.0.6"/> + <unit id="org.eclipse.jetty.util.ajax" version="10.0.6"/> + <unit id="org.eclipse.jetty.util.ajax.source" version="10.0.6"/> + <repository id="jetty-10.0.x" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/> + </location> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="com.google.gson" version="2.8.9.v20220111-1409"/> + <unit id="com.google.gson.source" version="2.8.9.v20220111-1409"/> + <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/> + <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/> + <unit id="com.jcraft.jzlib" version="1.1.3.v20220502-1820"/> + <unit id="com.jcraft.jzlib.source" version="1.1.3.v20220502-1820"/> + <unit id="com.sun.jna" version="5.8.0.v20210503-0343"/> + <unit id="com.sun.jna.source" version="5.8.0.v20210503-0343"/> + <unit id="com.sun.jna.platform" version="5.8.0.v20210406-1004"/> + <unit id="com.sun.jna.platform.source" version="5.8.0.v20210406-1004"/> + <unit id="javaewah" version="1.1.13.v20211029-0839"/> + <unit id="javaewah.source" version="1.1.13.v20211029-0839"/> + <unit id="net.bytebuddy.byte-buddy" version="1.9.0.v20181107-1410"/> + <unit id="net.bytebuddy.byte-buddy-agent" version="1.9.0.v20181106-1534"/> + <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.9.0.v20181106-1534"/> + <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20220506-1020"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20220506-1020"/> + <unit id="org.apache.ant" version="1.10.12.v20211102-1452"/> + <unit id="org.apache.ant.source" version="1.10.12.v20211102-1452"/> + <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/> + <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/> + <unit id="org.apache.commons.compress" version="1.21.0.v20211103-2100"/> + <unit id="org.apache.commons.compress.source" version="1.21.0.v20211103-2100"/> + <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/> + <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/> + <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/> + <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/> + <unit id="org.apache.httpcomponents.httpcore" version="4.4.15.v20220209-2345"/> + <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.15.v20220209-2345"/> + <unit id="org.apache.sshd.osgi" version="2.8.0.v20211227-1750"/> + <unit id="org.apache.sshd.osgi.source" version="2.8.0.v20211227-1750"/> + <unit id="org.apache.sshd.sftp" version="2.8.0.v20211227-1750"/> + <unit id="org.apache.sshd.sftp.source" version="2.8.0.v20211227-1750"/> + <unit id="org.assertj" version="3.20.2.v20210706-1104"/> + <unit id="org.assertj.source" version="3.20.2.v20210706-1104"/> + <unit id="org.bouncycastle.bcpg" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcpg.source" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcpkix" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcpkix.source" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcprov" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcprov.source" version="1.70.0.v20220507-1208"/> + <unit id="org.bouncycastle.bcutil" version="1.70.0.v20220105-1522"/> + <unit id="org.bouncycastle.bcutil.source" version="1.70.0.v20220105-1522"/> + <unit id="org.hamcrest" version="2.2.0.v20210711-0821"/> + <unit id="org.hamcrest.source" version="2.2.0.v20210711-0821"/> + <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/> + <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/> + <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/> + <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/> + <unit id="org.junit" version="4.13.2.v20211018-1956"/> + <unit id="org.junit.source" version="4.13.2.v20211018-1956"/> + <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> + <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> + <unit id="org.mockito" version="2.23.0.v20200310-1642"/> + <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/> + <unit id="org.objenesis" version="2.6.0.v20180420-1519"/> + <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/> + <unit id="org.slf4j.api" version="1.7.30.v20200204-2150"/> + <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/> + <unit id="org.slf4j.binding.simple" version="1.7.30.v20200204-2150"/> + <unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/> + <unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/> + <unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/> + <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository"/> + </location> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="org.eclipse.osgi" version="0.0.0"/> + <repository location="https://download.eclipse.org/staging/2022-06/"/> + </location> + </locations> +</target> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd new file mode 100644 index 0000000000..e81eec9eb6 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd @@ -0,0 +1,8 @@ +target "jgit-4.24" with source configurePhase + +include "projects/jetty-10.0.x.tpd" +include "orbit/R20220531185310-2022-06.tpd" + +location "https://download.eclipse.org/staging/2022-06/" { + org.eclipse.osgi lazy +} diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220531185310-2022-06.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220531185310-2022-06.tpd new file mode 100644 index 0000000000..3c74497c21 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220531185310-2022-06.tpd @@ -0,0 +1,69 @@ +target "R20220531185310-2022-06" with source configurePhase +// see https://download.eclipse.org/tools/orbit/downloads/ + +location "https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository" { + com.google.gson [2.8.9.v20220111-1409,2.8.9.v20220111-1409] + com.google.gson.source [2.8.9.v20220111-1409,2.8.9.v20220111-1409] + com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902] + com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902] + com.jcraft.jzlib [1.1.3.v20220502-1820,1.1.3.v20220502-1820] + com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820] + com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343] + com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343] + com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004] + com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004] + javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839] + javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839] + net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410] + net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534] + net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534] + net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410] + net.i2p.crypto.eddsa [0.3.0.v20220506-1020,0.3.0.v20220506-1020] + net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020] + org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452] + org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452] + org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422] + org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422] + org.apache.commons.compress [1.21.0.v20211103-2100,1.21.0.v20211103-2100] + org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100] + org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502] + org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502] + org.apache.httpcomponents.httpclient [4.5.13.v20210128-2225,4.5.13.v20210128-2225] + org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225] + org.apache.httpcomponents.httpcore [4.4.15.v20220209-2345,4.4.15.v20220209-2345] + org.apache.httpcomponents.httpcore.source [4.4.15.v20220209-2345,4.4.15.v20220209-2345] + org.apache.sshd.osgi [2.8.0.v20211227-1750,2.8.0.v20211227-1750] + org.apache.sshd.osgi.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750] + org.apache.sshd.sftp [2.8.0.v20211227-1750,2.8.0.v20211227-1750] + org.apache.sshd.sftp.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750] + org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104] + org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104] + org.bouncycastle.bcpg [1.70.0.v20220507-1208,1.70.0.v20220507-1208] + org.bouncycastle.bcpg.source [1.70.0.v20220507-1208,1.70.0.v20220507-1208] + org.bouncycastle.bcpkix [1.70.0.v20220105-1522,1.70.0.v20220105-1522] + org.bouncycastle.bcpkix.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522] + org.bouncycastle.bcprov [1.70.0.v20220507-1208,1.70.0.v20220507-1208] + org.bouncycastle.bcprov.source [1.70.0.v20220507-1208,1.70.0.v20220507-1208] + org.bouncycastle.bcutil [1.70.0.v20220105-1522,1.70.0.v20220105-1522] + org.bouncycastle.bcutil.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522] + org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821] + org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821] + org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519] + org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519] + org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246] + org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246] + org.junit [4.13.2.v20211018-1956,4.13.2.v20211018-1956] + org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956] + org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218] + org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218] + org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642] + org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642] + org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519] + org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519] + org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150] + org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150] + org.slf4j.binding.simple [1.7.30.v20200204-2150,1.7.30.v20200204-2150] + org.slf4j.binding.simple.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150] + org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259] + org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259] +} diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml index 2a95042eed..2296e95ed8 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml @@ -16,37 +16,10 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.target</artifactId> <packaging>pom</packaging> <name>JGit Target Platform</name> - - <build> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <executions> - <execution> - <id>attach-artifacts</id> - <phase>package</phase> - <goals> - <goal>attach-artifact</goal> - </goals> - <configuration> - <artifacts> - <artifact> - <file>${target-platform}.target</file> - <type>target</type> - <classifier>${target-platform}</classifier> - </artifact> - </artifacts> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> </project>
\ No newline at end of file diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml index b6505980e5..bfcfd65c21 100644 --- a/org.eclipse.jgit.packaging/pom.xml +++ b/org.eclipse.jgit.packaging/pom.xml @@ -16,14 +16,14 @@ <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> <packaging>pom</packaging> <name>JGit Tycho Parent</name> <properties> <java.version>11</java.version> - <tycho-version>2.5.0</tycho-version> + <tycho-version>2.6.0</tycho-version> <tycho-extras-version>${tycho-version}</tycho-extras-version> <target-platform>jgit-4.17</target-platform> </properties> @@ -231,12 +231,7 @@ <resolver>p2</resolver> <pomDependencies>consider</pomDependencies> <target> - <artifact> - <groupId>org.eclipse.jgit</groupId> - <artifactId>org.eclipse.jgit.target</artifactId> - <version>${project.version}</version> - <classifier>${target-platform}</classifier> - </artifact> + <file>${project.basedir}/../org.eclipse.jgit.target/${target-platform}.target</file> </target> <environments> <environment> diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF index f5c4de477c..c1b6ae6422 100644 --- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF @@ -3,31 +3,31 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.pgm.test Bundle-SymbolicName: org.eclipse.jgit.pgm.test -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 -Import-Package: org.eclipse.jgit.api;version="[6.1.1,6.2.0)", - org.eclipse.jgit.api.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.diff;version="[6.1.1,6.2.0)", - org.eclipse.jgit.dircache;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.diffmergetool;version="6.1.1", - org.eclipse.jgit.internal.storage.file;version="6.1.1", - org.eclipse.jgit.junit;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.merge;version="[6.1.1,6.2.0)", - org.eclipse.jgit.pgm;version="[6.1.1,6.2.0)", - org.eclipse.jgit.pgm.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.pgm.opt;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.treewalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util.io;version="[6.1.1,6.2.0)", - org.hamcrest.core;bundle-version="[2.2.0,3.0.0)", +Import-Package: org.eclipse.jgit.api;version="[6.2.0,6.3.0)", + org.eclipse.jgit.api.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.diff;version="[6.2.0,6.3.0)", + org.eclipse.jgit.dircache;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.diffmergetool;version="6.2.0", + org.eclipse.jgit.internal.storage.file;version="6.2.0", + org.eclipse.jgit.junit;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.merge;version="[6.2.0,6.3.0)", + org.eclipse.jgit.pgm;version="[6.2.0,6.3.0)", + org.eclipse.jgit.pgm.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.pgm.opt;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.treewalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util.io;version="[6.2.0,6.3.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)", org.kohsuke.args4j;version="[2.33.0,3.0.0)" diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml index c824788255..d26919a4d6 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.pgm.test</artifactId> diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java index e7bf48417d..2b50d45253 100644 --- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021, Simeon Andreev <simeon.danailov.andreev@gmail.com> and others. + * Copyright (C) 2021-2022, Simeon Andreev <simeon.danailov.andreev@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 @@ -9,72 +9,145 @@ */ package org.eclipse.jgit.pgm; -import static org.junit.Assert.assertEquals; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFFTOOL_SECTION; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CMD; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL; +import static org.junit.Assert.fail; +import java.io.File; +import java.io.InputStream; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.diff.DiffEntry; -import org.eclipse.jgit.internal.diffmergetool.CommandLineDiffTool; -import org.eclipse.jgit.lib.CLIRepositoryTestCase; -import org.eclipse.jgit.pgm.opt.CmdLineParser; -import org.eclipse.jgit.pgm.opt.SubcommandHandler; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.treewalk.FileTreeIterator; -import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.internal.diffmergetool.DiffTools; +import org.eclipse.jgit.internal.diffmergetool.ExternalDiffTool; +import org.eclipse.jgit.lib.StoredConfig; import org.junit.Before; import org.junit.Test; -import org.kohsuke.args4j.Argument; /** * Testing the {@code difftool} command. */ -public class DiffToolTest extends CLIRepositoryTestCase { - public static class GitCliJGitWrapperParser { - @Argument(index = 0, metaVar = "metaVar_command", required = true, handler = SubcommandHandler.class) - TextBuiltin subcommand; +public class DiffToolTest extends ToolTestCase { - @Argument(index = 1, metaVar = "metaVar_arg") - List<String> arguments = new ArrayList<>(); - } + private static final String DIFF_TOOL = CONFIG_DIFFTOOL_SECTION; - private String[] runAndCaptureUsingInitRaw(String... args) - throws Exception { - CLIGitCommand.Result result = new CLIGitCommand.Result(); + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + configureEchoTool(TOOL_NAME); + } - GitCliJGitWrapperParser bean = new GitCliJGitWrapperParser(); - CmdLineParser clp = new CmdLineParser(bean); - clp.parseArgument(args); + @Test(expected = Die.class) + public void testUndefinedTool() throws Exception { + String toolName = "undefined"; + String[] conflictingFilenames = createUnstagedChanges(); - TextBuiltin cmd = bean.subcommand; - cmd.initRaw(db, null, null, result.out, result.err); - cmd.execute(bean.arguments.toArray(new String[bean.arguments.size()])); - if (cmd.getOutputWriter() != null) { - cmd.getOutputWriter().flush(); + List<String> expectedErrors = new ArrayList<>(); + for (String changedFilename : conflictingFilenames) { + expectedErrors.add("External diff tool is not defined: " + toolName); + expectedErrors.add("compare of " + changedFilename + " failed"); } - if (cmd.getErrorWriter() != null) { - cmd.getErrorWriter().flush(); - } - return result.outLines().toArray(new String[0]); + + runAndCaptureUsingInitRaw(expectedErrors, DIFF_TOOL, "--no-prompt", + "--tool", toolName); + fail("Expected exception to be thrown due to undefined external tool"); } - private Git git; + @Test(expected = Die.class) + public void testUserToolWithCommandNotFoundError() throws Exception { + String toolName = "customTool"; - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - git = new Git(db); - git.commit().setMessage("initial commit").call(); + int errorReturnCode = 127; // command not found + String command = "exit " + errorReturnCode; + + StoredConfig config = db.getConfig(); + config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD, + command); + + createMergeConflict(); + runAndCaptureUsingInitRaw(DIFF_TOOL, "--no-prompt", "--tool", toolName); + + fail("Expected exception to be thrown due to external tool exiting with error code: " + + errorReturnCode); + } + + @Test(expected = Die.class) + public void testEmptyToolName() throws Exception { + String emptyToolName = ""; + + StoredConfig config = db.getConfig(); + // the default diff tool is configured without a subsection + String subsection = null; + config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_TOOL, + emptyToolName); + + createUnstagedChanges(); + + String araxisErrorLine = "compare: unrecognized option `-wait' @ error/compare.c/CompareImageCommand/1123."; + String[] expectedErrorOutput = { araxisErrorLine, araxisErrorLine, }; + runAndCaptureUsingInitRaw(Arrays.asList(expectedErrorOutput), DIFF_TOOL, + "--no-prompt"); + fail("Expected exception to be thrown due to external tool exiting with an error"); + } + + @Test + public void testToolWithPrompt() throws Exception { + String[] inputLines = { + "y", // accept launching diff tool + "y", // accept launching diff tool + }; + + String[] conflictingFilenames = createUnstagedChanges(); + String[] expectedOutput = getExpectedCompareOutput(conflictingFilenames); + + String option = "--tool"; + + InputStream inputStream = createInputStream(inputLines); + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, runAndCaptureUsingInitRaw(inputStream, + DIFF_TOOL, "--prompt", option, TOOL_NAME)); + } + + @Test + public void testToolAbortLaunch() throws Exception { + String[] inputLines = { + "y", // accept launching diff tool + "n", // don't launch diff tool + }; + + String[] conflictingFilenames = createUnstagedChanges(); + int abortIndex = 1; + String[] expectedOutput = getExpectedAbortOutput(conflictingFilenames, abortIndex); + + String option = "--tool"; + + InputStream inputStream = createInputStream(inputLines); + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, + runAndCaptureUsingInitRaw(inputStream, DIFF_TOOL, "--prompt", option, + TOOL_NAME)); + } + + @Test(expected = Die.class) + public void testNotDefinedTool() throws Exception { + createUnstagedChanges(); + + runAndCaptureUsingInitRaw(DIFF_TOOL, "--tool", "undefined"); + fail("Expected exception when trying to run undefined tool"); } @Test public void testTool() throws Exception { - RevCommit commit = createUnstagedChanges(); - List<DiffEntry> changes = getRepositoryChanges(commit); - String[] expectedOutput = getExpectedDiffToolOutput(changes); + String[] conflictFilenames = createUnstagedChanges(); + String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictFilenames); String[] options = { "--tool", @@ -84,69 +157,88 @@ public class DiffToolTest extends CLIRepositoryTestCase { for (String option : options) { assertArrayOfLinesEquals("Incorrect output for option: " + option, expectedOutput, - runAndCaptureUsingInitRaw("difftool", option, - "some_tool")); + runAndCaptureUsingInitRaw(DIFF_TOOL, option, + TOOL_NAME)); } } @Test public void testToolTrustExitCode() throws Exception { - RevCommit commit = createUnstagedChanges(); - List<DiffEntry> changes = getRepositoryChanges(commit); - String[] expectedOutput = getExpectedDiffToolOutput(changes); + String[] conflictingFilenames = createUnstagedChanges(); + String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictingFilenames); String[] options = { "--tool", "-t", }; for (String option : options) { assertArrayOfLinesEquals("Incorrect output for option: " + option, - expectedOutput, runAndCaptureUsingInitRaw("difftool", - "--trust-exit-code", option, "some_tool")); + expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL, + "--trust-exit-code", option, TOOL_NAME)); } } @Test public void testToolNoGuiNoPromptNoTrustExitcode() throws Exception { - RevCommit commit = createUnstagedChanges(); - List<DiffEntry> changes = getRepositoryChanges(commit); - String[] expectedOutput = getExpectedDiffToolOutput(changes); + String[] conflictingFilenames = createUnstagedChanges(); + String[] expectedOutput = getExpectedToolOutputNoPrompt(conflictingFilenames); String[] options = { "--tool", "-t", }; for (String option : options) { assertArrayOfLinesEquals("Incorrect output for option: " + option, - expectedOutput, runAndCaptureUsingInitRaw("difftool", + expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL, "--no-gui", "--no-prompt", "--no-trust-exit-code", - option, "some_tool")); + option, TOOL_NAME)); } } @Test public void testToolCached() throws Exception { - RevCommit commit = createStagedChanges(); - List<DiffEntry> changes = getRepositoryChanges(commit); - String[] expectedOutput = getExpectedDiffToolOutput(changes); + String[] conflictingFilenames = createStagedChanges(); + Pattern[] expectedOutput = getExpectedCachedToolOutputNoPrompt(conflictingFilenames); String[] options = { "--cached", "--staged", }; for (String option : options) { - assertArrayOfLinesEquals("Incorrect output for option: " + option, - expectedOutput, runAndCaptureUsingInitRaw("difftool", - option, "--tool", "some_tool")); + assertArrayOfMatchingLines("Incorrect output for option: " + option, + expectedOutput, runAndCaptureUsingInitRaw(DIFF_TOOL, + option, "--tool", TOOL_NAME)); } } @Test public void testToolHelp() throws Exception { - CommandLineDiffTool[] defaultTools = CommandLineDiffTool.values(); List<String> expectedOutput = new ArrayList<>(); - expectedOutput.add("git difftool --tool=<tool> may be set to one of the following:"); - for (CommandLineDiffTool defaultTool : defaultTools) { - String toolName = defaultTool.name(); + + DiffTools diffTools = new DiffTools(db); + Map<String, ExternalDiffTool> predefinedTools = diffTools + .getPredefinedTools(true); + List<ExternalDiffTool> availableTools = new ArrayList<>(); + List<ExternalDiffTool> notAvailableTools = new ArrayList<>(); + for (ExternalDiffTool tool : predefinedTools.values()) { + if (tool.isAvailable()) { + availableTools.add(tool); + } else { + notAvailableTools.add(tool); + } + } + + expectedOutput.add( + "'git difftool --tool=<tool>' may be set to one of the following:"); + for (ExternalDiffTool tool : availableTools) { + String toolName = tool.getName(); + expectedOutput.add(toolName); + } + String customToolHelpLine = TOOL_NAME + "." + CONFIG_KEY_CMD + " " + + getEchoCommand(); + expectedOutput.add("user-defined:"); + expectedOutput.add(customToolHelpLine); + expectedOutput.add( + "The following tools are valid, but not currently available:"); + for (ExternalDiffTool tool : notAvailableTools) { + String toolName = tool.getName(); expectedOutput.add(toolName); } String[] userDefinedToolsHelp = { - "user-defined:", - "The following tools are valid, but not currently available:", "Some of the tools listed above only work in a windowed", "environment. If run in a terminal-only session, they will fail.", }; @@ -154,52 +246,99 @@ public class DiffToolTest extends CLIRepositoryTestCase { String option = "--tool-help"; assertArrayOfLinesEquals("Incorrect output for option: " + option, - expectedOutput.toArray(new String[0]), runAndCaptureUsingInitRaw("difftool", option)); - } - - private RevCommit createUnstagedChanges() throws Exception { - writeTrashFile("a", "Hello world a"); - writeTrashFile("b", "Hello world b"); - git.add().addFilepattern(".").call(); - RevCommit commit = git.commit().setMessage("files a & b").call(); - writeTrashFile("a", "New Hello world a"); - writeTrashFile("b", "New Hello world b"); - return commit; - } - - private RevCommit createStagedChanges() throws Exception { - RevCommit commit = createUnstagedChanges(); - git.add().addFilepattern(".").call(); - return commit; - } - - private List<DiffEntry> getRepositoryChanges(RevCommit commit) - throws Exception { - TreeWalk tw = new TreeWalk(db); - tw.addTree(commit.getTree()); - FileTreeIterator modifiedTree = new FileTreeIterator(db); - tw.addTree(modifiedTree); - List<DiffEntry> changes = DiffEntry.scan(tw); - return changes; - } - - private String[] getExpectedDiffToolOutput(List<DiffEntry> changes) { - String[] expectedToolOutput = new String[changes.size()]; - for (int i = 0; i < changes.size(); ++i) { - DiffEntry change = changes.get(i); - String newPath = change.getNewPath(); - String oldPath = change.getOldPath(); - String newIdName = change.getNewId().name(); - String oldIdName = change.getOldId().name(); - String expectedLine = "M\t" + newPath + " (" + newIdName + ")" - + "\t" + oldPath + " (" + oldIdName + ")"; - expectedToolOutput[i] = expectedLine; + expectedOutput.toArray(new String[0]), + runAndCaptureUsingInitRaw(DIFF_TOOL, option)); + } + + private void configureEchoTool(String toolName) { + StoredConfig config = db.getConfig(); + // the default diff tool is configured without a subsection + String subsection = null; + config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_TOOL, + toolName); + + String command = getEchoCommand(); + + config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD, + command); + /* + * prevent prompts as we are running in tests and there is no user to + * interact with on the command line + */ + config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_PROMPT, + String.valueOf(false)); + } + + private String[] getExpectedToolOutputNoPrompt(String[] conflictingFilenames) { + String[] expectedToolOutput = new String[conflictingFilenames.length]; + for (int i = 0; i < conflictingFilenames.length; ++i) { + String newPath = conflictingFilenames[i]; + Path fullPath = getFullPath(newPath); + expectedToolOutput[i] = fullPath.toString(); } return expectedToolOutput; } - private static void assertArrayOfLinesEquals(String failMessage, - String[] expected, String[] actual) { - assertEquals(failMessage, toString(expected), toString(actual)); + private Pattern[] getExpectedCachedToolOutputNoPrompt(String[] conflictingFilenames) { + String tmpDir = System.getProperty("java.io.tmpdir"); + if (tmpDir.endsWith(File.separator)) { + tmpDir = tmpDir.substring(0, tmpDir.length() - 1); + } + Pattern emptyPattern = Pattern.compile(""); + List<Pattern> expectedToolOutput = new ArrayList<>(); + for (int i = 0; i < conflictingFilenames.length; ++i) { + String changedFilename = conflictingFilenames[i]; + Path fullPath = getFullPath(changedFilename); + String filename = fullPath.getFileName().toString(); + String regexp = tmpDir + File.separatorChar + filename + + "_REMOTE_.*"; + Pattern pattern = Pattern.compile(regexp); + expectedToolOutput.add(pattern); + expectedToolOutput.add(emptyPattern); + } + expectedToolOutput.add(emptyPattern); + return expectedToolOutput.toArray(new Pattern[0]); + } + + private String[] getExpectedCompareOutput(String[] conflictingFilenames) { + List<String> expected = new ArrayList<>(); + int n = conflictingFilenames.length; + for (int i = 0; i < n; ++i) { + String changedFilename = conflictingFilenames[i]; + expected.add( + "Viewing (" + (i + 1) + "/" + n + "): '" + changedFilename + + "'"); + expected.add("Launch '" + TOOL_NAME + "' [Y/n]?"); + Path fullPath = getFullPath(changedFilename); + expected.add(fullPath.toString()); + } + return expected.toArray(new String[0]); + } + + private String[] getExpectedAbortOutput(String[] conflictingFilenames, + int abortIndex) { + List<String> expected = new ArrayList<>(); + int n = conflictingFilenames.length; + for (int i = 0; i < n; ++i) { + String changedFilename = conflictingFilenames[i]; + expected.add( + "Viewing (" + (i + 1) + "/" + n + "): '" + changedFilename + + "'"); + expected.add("Launch '" + TOOL_NAME + "' [Y/n]?"); + if (i == abortIndex) { + break; + } + Path fullPath = getFullPath(changedFilename); + expected.add(fullPath.toString()); + } + return expected.toArray(new String[0]); + } + + private static String getEchoCommand() { + /* + * use 'REMOTE' placeholder, as it will be replaced by a file path + * within the repository. + */ + return "(echo \"$REMOTE\")"; } } diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java new file mode 100644 index 0000000000..1236dd30d3 --- /dev/null +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2022, Simeon Andreev <simeon.danailov.andreev@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.pgm; + +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CMD; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGETOOL_SECTION; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION; +import static org.junit.Assert.fail; + +import java.io.InputStream; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.eclipse.jgit.internal.diffmergetool.ExternalMergeTool; +import org.eclipse.jgit.internal.diffmergetool.MergeTools; +import org.eclipse.jgit.lib.StoredConfig; +import org.junit.Before; +import org.junit.Test; + +/** + * Testing the {@code mergetool} command. + */ +public class MergeToolTest extends ToolTestCase { + + private static final String MERGE_TOOL = CONFIG_MERGETOOL_SECTION; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + configureEchoTool(TOOL_NAME); + } + + @Test + public void testUndefinedTool() throws Exception { + String toolName = "undefined"; + String[] conflictingFilenames = createMergeConflict(); + + List<String> expectedErrors = new ArrayList<>(); + for (String conflictingFilename : conflictingFilenames) { + expectedErrors.add("External merge tool is not defined: " + toolName); + expectedErrors.add("merge of " + conflictingFilename + " failed"); + } + + runAndCaptureUsingInitRaw(expectedErrors, MERGE_TOOL, + "--no-prompt", "--tool", toolName); + } + + @Test(expected = Die.class) + public void testUserToolWithCommandNotFoundError() throws Exception { + String toolName = "customTool"; + + int errorReturnCode = 127; // command not found + String command = "exit " + errorReturnCode; + + StoredConfig config = db.getConfig(); + config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD, + command); + + createMergeConflict(); + runAndCaptureUsingInitRaw(MERGE_TOOL, "--no-prompt", "--tool", + toolName); + + fail("Expected exception to be thrown due to external tool exiting with error code: " + + errorReturnCode); + } + + @Test + public void testEmptyToolName() throws Exception { + String emptyToolName = ""; + + StoredConfig config = db.getConfig(); + // the default merge tool is configured without a subsection + String subsection = null; + config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_TOOL, + emptyToolName); + + createMergeConflict(); + + String araxisErrorLine = "compare: unrecognized option `-wait' @ error/compare.c/CompareImageCommand/1123."; + String[] expectedErrorOutput = { araxisErrorLine, araxisErrorLine, }; + runAndCaptureUsingInitRaw(Arrays.asList(expectedErrorOutput), + MERGE_TOOL, "--no-prompt"); + } + + @Test + public void testAbortMerge() throws Exception { + String[] inputLines = { + "y", // start tool for merge resolution + "n", // don't accept merge tool result + "n", // don't continue resolution + }; + String[] conflictingFilenames = createMergeConflict(); + int abortIndex = 1; + String[] expectedOutput = getExpectedAbortMergeOutput( + conflictingFilenames, + abortIndex); + + String option = "--tool"; + + InputStream inputStream = createInputStream(inputLines); + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, runAndCaptureUsingInitRaw(inputStream, + MERGE_TOOL, "--prompt", option, TOOL_NAME)); + } + + @Test + public void testAbortLaunch() throws Exception { + String[] inputLines = { + "n", // abort merge tool launch + }; + String[] conflictingFilenames = createMergeConflict(); + String[] expectedOutput = getExpectedAbortLaunchOutput( + conflictingFilenames); + + String option = "--tool"; + + InputStream inputStream = createInputStream(inputLines); + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, runAndCaptureUsingInitRaw(inputStream, + MERGE_TOOL, "--prompt", option, TOOL_NAME)); + } + + @Test + public void testMergeConflict() throws Exception { + String[] inputLines = { + "y", // start tool for merge resolution + "y", // accept merge result as successful + "y", // start tool for merge resolution + "y", // accept merge result as successful + }; + String[] conflictingFilenames = createMergeConflict(); + String[] expectedOutput = getExpectedMergeConflictOutput( + conflictingFilenames); + + String option = "--tool"; + + InputStream inputStream = createInputStream(inputLines); + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, runAndCaptureUsingInitRaw(inputStream, + MERGE_TOOL, "--prompt", option, TOOL_NAME)); + } + + @Test + public void testDeletedConflict() throws Exception { + String[] inputLines = { + "d", // choose delete option to resolve conflict + "m", // choose merge option to resolve conflict + }; + String[] conflictingFilenames = createDeletedConflict(); + String[] expectedOutput = getExpectedDeletedConflictOutput( + conflictingFilenames); + + String option = "--tool"; + + InputStream inputStream = createInputStream(inputLines); + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, runAndCaptureUsingInitRaw(inputStream, + MERGE_TOOL, "--prompt", option, TOOL_NAME)); + } + + @Test + public void testNoConflict() throws Exception { + createStagedChanges(); + String[] expectedOutput = { "No files need merging" }; + + String[] options = { "--tool", "-t", }; + + for (String option : options) { + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, + runAndCaptureUsingInitRaw(MERGE_TOOL, option, TOOL_NAME)); + } + } + + @Test + public void testMergeConflictNoPrompt() throws Exception { + String[] conflictingFilenames = createMergeConflict(); + String[] expectedOutput = getExpectedMergeConflictOutputNoPrompt( + conflictingFilenames); + + String option = "--tool"; + + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, + runAndCaptureUsingInitRaw(MERGE_TOOL, option, TOOL_NAME)); + } + + @Test + public void testMergeConflictNoGuiNoPrompt() throws Exception { + String[] conflictingFilenames = createMergeConflict(); + String[] expectedOutput = getExpectedMergeConflictOutputNoPrompt( + conflictingFilenames); + + String option = "--tool"; + + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, runAndCaptureUsingInitRaw(MERGE_TOOL, + "--no-gui", "--no-prompt", option, TOOL_NAME)); + } + + @Test + public void testToolHelp() throws Exception { + List<String> expectedOutput = new ArrayList<>(); + + MergeTools diffTools = new MergeTools(db); + Map<String, ExternalMergeTool> predefinedTools = diffTools + .getPredefinedTools(true); + List<ExternalMergeTool> availableTools = new ArrayList<>(); + List<ExternalMergeTool> notAvailableTools = new ArrayList<>(); + for (ExternalMergeTool tool : predefinedTools.values()) { + if (tool.isAvailable()) { + availableTools.add(tool); + } else { + notAvailableTools.add(tool); + } + } + + expectedOutput.add( + "'git mergetool --tool=<tool>' may be set to one of the following:"); + for (ExternalMergeTool tool : availableTools) { + String toolName = tool.getName(); + expectedOutput.add(toolName); + } + String customToolHelpLine = TOOL_NAME + "." + CONFIG_KEY_CMD + " " + + getEchoCommand(); + expectedOutput.add("user-defined:"); + expectedOutput.add(customToolHelpLine); + expectedOutput.add( + "The following tools are valid, but not currently available:"); + for (ExternalMergeTool tool : notAvailableTools) { + String toolName = tool.getName(); + expectedOutput.add(toolName); + } + String[] userDefinedToolsHelp = { + "Some of the tools listed above only work in a windowed", + "environment. If run in a terminal-only session, they will fail.", }; + expectedOutput.addAll(Arrays.asList(userDefinedToolsHelp)); + + String option = "--tool-help"; + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput.toArray(new String[0]), + runAndCaptureUsingInitRaw(MERGE_TOOL, option)); + } + + private void configureEchoTool(String toolName) { + StoredConfig config = db.getConfig(); + // the default merge tool is configured without a subsection + String subsection = null; + config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_TOOL, + toolName); + + String command = getEchoCommand(); + + config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD, + command); + /* + * prevent prompts as we are running in tests and there is no user to + * interact with on the command line + */ + config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_PROMPT, + String.valueOf(false)); + } + + private String[] getExpectedMergeConflictOutputNoPrompt( + String[] conflictFilenames) { + List<String> expected = new ArrayList<>(); + expected.add("Merging:"); + for (String conflictFilename : conflictFilenames) { + expected.add(conflictFilename); + } + for (String conflictFilename : conflictFilenames) { + expected.add("Normal merge conflict for '" + conflictFilename + + "':"); + expected.add("{local}: modified file"); + expected.add("{remote}: modified file"); + Path filePath = getFullPath(conflictFilename); + expected.add(filePath.toString()); + expected.add(conflictFilename + " seems unchanged."); + } + return expected.toArray(new String[0]); + } + + private static String[] getExpectedAbortLaunchOutput( + String[] conflictFilenames) { + List<String> expected = new ArrayList<>(); + expected.add("Merging:"); + for (String conflictFilename : conflictFilenames) { + expected.add(conflictFilename); + } + if (conflictFilenames.length > 1) { + String conflictFilename = conflictFilenames[0]; + expected.add( + "Normal merge conflict for '" + conflictFilename + "':"); + expected.add("{local}: modified file"); + expected.add("{remote}: modified file"); + expected.add("Hit return to start merge resolution tool (" + + TOOL_NAME + "):"); + } + return expected.toArray(new String[0]); + } + + private String[] getExpectedAbortMergeOutput( + String[] conflictFilenames, int abortIndex) { + List<String> expected = new ArrayList<>(); + expected.add("Merging:"); + for (String conflictFilename : conflictFilenames) { + expected.add(conflictFilename); + } + for (int i = 0; i < conflictFilenames.length; ++i) { + if (i == abortIndex) { + break; + } + + String conflictFilename = conflictFilenames[i]; + expected.add( + "Normal merge conflict for '" + conflictFilename + "':"); + expected.add("{local}: modified file"); + expected.add("{remote}: modified file"); + Path fullPath = getFullPath(conflictFilename); + expected.add("Hit return to start merge resolution tool (" + + TOOL_NAME + "): " + fullPath); + expected.add(conflictFilename + " seems unchanged."); + expected.add("Was the merge successful [y/n]?"); + if (i < conflictFilenames.length - 1) { + expected.add( + "\tContinue merging other unresolved paths [y/n]?"); + } + } + return expected.toArray(new String[0]); + } + + private String[] getExpectedMergeConflictOutput( + String[] conflictFilenames) { + List<String> expected = new ArrayList<>(); + expected.add("Merging:"); + for (String conflictFilename : conflictFilenames) { + expected.add(conflictFilename); + } + for (int i = 0; i < conflictFilenames.length; ++i) { + String conflictFilename = conflictFilenames[i]; + expected.add("Normal merge conflict for '" + conflictFilename + + "':"); + expected.add("{local}: modified file"); + expected.add("{remote}: modified file"); + Path filePath = getFullPath(conflictFilename); + expected.add("Hit return to start merge resolution tool (" + + TOOL_NAME + "): " + filePath); + expected.add(conflictFilename + " seems unchanged."); + expected.add("Was the merge successful [y/n]?"); + if (i < conflictFilenames.length - 1) { + // expected.add( + // "\tContinue merging other unresolved paths [y/n]?"); + } + } + return expected.toArray(new String[0]); + } + + private static String[] getExpectedDeletedConflictOutput( + String[] conflictFilenames) { + List<String> expected = new ArrayList<>(); + expected.add("Merging:"); + for (String mergeConflictFilename : conflictFilenames) { + expected.add(mergeConflictFilename); + } + for (int i = 0; i < conflictFilenames.length; ++i) { + String conflictFilename = conflictFilenames[i]; + expected.add(conflictFilename + " seems unchanged."); + expected.add("{local}: deleted"); + expected.add("{remote}: modified file"); + expected.add("Use (m)odified or (d)eleted file, or (a)bort?"); + } + return expected.toArray(new String[0]); + } + + private static String getEchoCommand() { + /* + * use 'MERGED' placeholder, as both 'LOCAL' and 'REMOTE' will be + * replaced with full paths to a temporary file during some of the tests + */ + return "(echo \"$MERGED\")"; + } +} diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ToolTestCase.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ToolTestCase.java new file mode 100644 index 0000000000..a3c41f0fed --- /dev/null +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ToolTestCase.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2022, Simeon Andreev <simeon.danailov.andreev@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.pgm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.lib.CLIRepositoryTestCase; +import org.eclipse.jgit.pgm.opt.CmdLineParser; +import org.eclipse.jgit.pgm.opt.SubcommandHandler; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.junit.Before; +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.CmdLineException; + +/** + * Base test case for the {@code difftool} and {@code mergetool} commands. + */ +public abstract class ToolTestCase extends CLIRepositoryTestCase { + + public static class GitCliJGitWrapperParser { + @Argument(index = 0, metaVar = "metaVar_command", required = true, handler = SubcommandHandler.class) + TextBuiltin subcommand; + + @Argument(index = 1, metaVar = "metaVar_arg") + List<String> arguments = new ArrayList<>(); + } + + protected static final String TOOL_NAME = "some_tool"; + + private static final String TEST_BRANCH_NAME = "test_branch"; + + private Git git; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + git = new Git(db); + git.commit().setMessage("initial commit").call(); + git.branchCreate().setName(TEST_BRANCH_NAME).call(); + } + + protected String[] runAndCaptureUsingInitRaw(String... args) + throws Exception { + InputStream inputStream = null; // no input stream + return runAndCaptureUsingInitRaw(inputStream, args); + } + + protected String[] runAndCaptureUsingInitRaw( + List<String> expectedErrorOutput, String... args) throws Exception { + InputStream inputStream = null; // no input stream + return runAndCaptureUsingInitRaw(inputStream, expectedErrorOutput, + args); + } + + protected String[] runAndCaptureUsingInitRaw(InputStream inputStream, + String... args) throws Exception { + List<String> expectedErrorOutput = Collections.emptyList(); + return runAndCaptureUsingInitRaw(inputStream, expectedErrorOutput, + args); + } + + protected String[] runAndCaptureUsingInitRaw(InputStream inputStream, + List<String> expectedErrorOutput, String... args) + throws CmdLineException, Exception, IOException { + CLIGitCommand.Result result = new CLIGitCommand.Result(); + + GitCliJGitWrapperParser bean = new GitCliJGitWrapperParser(); + CmdLineParser clp = new CmdLineParser(bean); + clp.parseArgument(args); + + TextBuiltin cmd = bean.subcommand; + cmd.initRaw(db, null, inputStream, result.out, result.err); + cmd.execute(bean.arguments.toArray(new String[bean.arguments.size()])); + if (cmd.getOutputWriter() != null) { + cmd.getOutputWriter().flush(); + } + if (cmd.getErrorWriter() != null) { + cmd.getErrorWriter().flush(); + } + + List<String> errLines = result.errLines().stream() + .filter(l -> !l.isBlank()) // we care only about error messages + .collect(Collectors.toList()); + assertEquals("Expected no standard error output from tool", + expectedErrorOutput.toString(), errLines.toString()); + + return result.outLines().toArray(new String[0]); + } + + protected String[] createMergeConflict() throws Exception { + // create files on initial branch + git.checkout().setName(TEST_BRANCH_NAME).call(); + writeTrashFile("dir1/a", "Hello world a"); + writeTrashFile("dir2/b", "Hello world b"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("files a & b added").call(); + // create another branch and change files + git.branchCreate().setName("branch_1").call(); + git.checkout().setName("branch_1").call(); + writeTrashFile("dir1/a", "Hello world a 1"); + writeTrashFile("dir2/b", "Hello world b 1"); + git.add().addFilepattern(".").call(); + RevCommit commit1 = git.commit() + .setMessage("files a & b modified commit 1").call(); + // checkout initial branch + git.checkout().setName(TEST_BRANCH_NAME).call(); + // create another branch and change files + git.branchCreate().setName("branch_2").call(); + git.checkout().setName("branch_2").call(); + writeTrashFile("dir1/a", "Hello world a 2"); + writeTrashFile("dir2/b", "Hello world b 2"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("files a & b modified commit 2").call(); + // cherry-pick conflicting changes + git.cherryPick().include(commit1).call(); + String[] conflictingFilenames = { "dir1/a", "dir2/b" }; + return conflictingFilenames; + } + + protected String[] createDeletedConflict() throws Exception { + // create files on initial branch + git.checkout().setName(TEST_BRANCH_NAME).call(); + writeTrashFile("dir1/a", "Hello world a"); + writeTrashFile("dir2/b", "Hello world b"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("files a & b added").call(); + // create another branch and change files + git.branchCreate().setName("branch_1").call(); + git.checkout().setName("branch_1").call(); + writeTrashFile("dir1/a", "Hello world a 1"); + writeTrashFile("dir2/b", "Hello world b 1"); + git.add().addFilepattern(".").call(); + RevCommit commit1 = git.commit() + .setMessage("files a & b modified commit 1").call(); + // checkout initial branch + git.checkout().setName(TEST_BRANCH_NAME).call(); + // create another branch and change files + git.branchCreate().setName("branch_2").call(); + git.checkout().setName("branch_2").call(); + git.rm().addFilepattern("dir1/a").call(); + git.rm().addFilepattern("dir2/b").call(); + git.commit().setMessage("files a & b deleted commit 2").call(); + // cherry-pick conflicting changes + git.cherryPick().include(commit1).call(); + String[] conflictingFilenames = { "dir1/a", "dir2/b" }; + return conflictingFilenames; + } + + protected String[] createUnstagedChanges() throws Exception { + writeTrashFile("dir1/a", "Hello world a"); + writeTrashFile("dir2/b", "Hello world b"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("files a & b").call(); + writeTrashFile("dir1/a", "New Hello world a"); + writeTrashFile("dir2/b", "New Hello world b"); + String[] conflictingFilenames = { "dir1/a", "dir2/b" }; + return conflictingFilenames; + } + + protected String[] createStagedChanges() throws Exception { + String[] conflictingFilenames = createUnstagedChanges(); + git.add().addFilepattern(".").call(); + return conflictingFilenames; + } + + protected List<DiffEntry> getRepositoryChanges(RevCommit commit) + throws Exception { + TreeWalk tw = new TreeWalk(db); + tw.addTree(commit.getTree()); + FileTreeIterator modifiedTree = new FileTreeIterator(db); + tw.addTree(modifiedTree); + List<DiffEntry> changes = DiffEntry.scan(tw); + return changes; + } + + protected Path getFullPath(String repositoryFilename) { + Path dotGitPath = db.getDirectory().toPath(); + Path repositoryRoot = dotGitPath.getParent(); + Path repositoryFilePath = repositoryRoot.resolve(repositoryFilename); + return repositoryFilePath; + } + + protected static InputStream createInputStream(String[] inputLines) { + return createInputStream(Arrays.asList(inputLines)); + } + + protected static InputStream createInputStream(List<String> inputLines) { + String input = String.join(System.lineSeparator(), inputLines); + InputStream inputStream = new ByteArrayInputStream(input.getBytes()); + return inputStream; + } + + protected static void assertArrayOfLinesEquals(String failMessage, + String[] expected, String[] actual) { + assertEquals(failMessage, toString(expected), toString(actual)); + } + + protected static void assertArrayOfMatchingLines(String failMessage, + Pattern[] expected, String[] actual) { + assertEquals(failMessage + System.lineSeparator() + + "Expected and actual lines count don't match. Expected: " + + Arrays.asList(expected) + ", actual: " + + Arrays.asList(actual), expected.length, actual.length); + int n = expected.length; + for (int i = 0; i < n; ++i) { + Pattern expectedPattern = expected[i]; + String actualLine = actual[i]; + Matcher matcher = expectedPattern.matcher(actualLine); + boolean matches = matcher.matches(); + assertTrue(failMessage + System.lineSeparator() + "Line " + i + " '" + + actualLine + "' doesn't match expected pattern: " + + expectedPattern + System.lineSeparator() + "Expected: " + + Arrays.asList(expected) + ", actual: " + + Arrays.asList(actual), + matches); + } + } +} diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF index ed980b5006..2ef2cd2ddb 100644 --- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.pgm Bundle-SymbolicName: org.eclipse.jgit.pgm -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-11 @@ -14,49 +14,49 @@ Import-Package: javax.servlet;version="[3.1.0,5.0.0)", org.eclipse.jetty.servlet;version="[10.0.0,11.0.0)", org.eclipse.jetty.util;version="[10.0.0,11.0.0)", org.eclipse.jetty.util.component;version="[10.0.0,11.0.0)", - org.eclipse.jgit.api;version="[6.1.1,6.2.0)", - org.eclipse.jgit.api.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.archive;version="[6.1.1,6.2.0)", - org.eclipse.jgit.awtui;version="[6.1.1,6.2.0)", - org.eclipse.jgit.blame;version="[6.1.1,6.2.0)", - org.eclipse.jgit.diff;version="[6.1.1,6.2.0)", - org.eclipse.jgit.dircache;version="[6.1.1,6.2.0)", - org.eclipse.jgit.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.gitrepo;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.diffmergetool;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.io;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.pack;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.reftable;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.server;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.server.fs;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs.server.s3;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.merge;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.nls;version="[6.1.1,6.2.0)", - org.eclipse.jgit.notes;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revplot;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk.filter;version="[6.1.1,6.2.0)", - org.eclipse.jgit.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.storage.pack;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.http.apache;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.resolver;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.ssh.jsch;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.sshd;version="[6.1.1,6.2.0)", - org.eclipse.jgit.treewalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.treewalk.filter;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util.io;version="[6.1.1,6.2.0)", + org.eclipse.jgit.api;version="[6.2.0,6.3.0)", + org.eclipse.jgit.api.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.archive;version="[6.2.0,6.3.0)", + org.eclipse.jgit.awtui;version="[6.2.0,6.3.0)", + org.eclipse.jgit.blame;version="[6.2.0,6.3.0)", + org.eclipse.jgit.diff;version="[6.2.0,6.3.0)", + org.eclipse.jgit.dircache;version="[6.2.0,6.3.0)", + org.eclipse.jgit.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.gitrepo;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.diffmergetool;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.io;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.pack;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.reftable;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.server;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.server.fs;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs.server.s3;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.merge;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.nls;version="[6.2.0,6.3.0)", + org.eclipse.jgit.notes;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revplot;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk.filter;version="[6.2.0,6.3.0)", + org.eclipse.jgit.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.storage.pack;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.http.apache;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.resolver;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.ssh.jsch;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.sshd;version="[6.2.0,6.3.0)", + org.eclipse.jgit.treewalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.treewalk.filter;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util.io;version="[6.2.0,6.3.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="6.1.1"; +Export-Package: org.eclipse.jgit.console;version="6.2.0"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util", - org.eclipse.jgit.pgm;version="6.1.1"; + org.eclipse.jgit.pgm;version="6.2.0"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util.io, org.eclipse.jgit.awtui, @@ -68,14 +68,14 @@ Export-Package: org.eclipse.jgit.console;version="6.1.1"; org.eclipse.jgit.treewalk, org.eclipse.jgit.api, javax.swing", - org.eclipse.jgit.pgm.debug;version="6.1.1"; + org.eclipse.jgit.pgm.debug;version="6.2.0"; uses:="org.eclipse.jgit.util.io, org.eclipse.jgit.pgm, org.eclipse.jetty.servlet", - org.eclipse.jgit.pgm.internal;version="6.1.1"; + org.eclipse.jgit.pgm.internal;version="6.2.0"; x-friends:="org.eclipse.jgit.pgm.test, org.eclipse.jgit.test", - org.eclipse.jgit.pgm.opt;version="6.1.1"; + org.eclipse.jgit.pgm.opt;version="6.2.0"; uses:="org.kohsuke.args4j, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF index 0a5dd88040..fd88286810 100644 --- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.pgm - Sources Bundle-SymbolicName: org.eclipse.jgit.pgm.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin index 8c44764c63..ea1d1e3faa 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 @@ -25,6 +25,7 @@ org.eclipse.jgit.pgm.LsRemote org.eclipse.jgit.pgm.LsTree org.eclipse.jgit.pgm.Merge org.eclipse.jgit.pgm.MergeBase +org.eclipse.jgit.pgm.MergeTool 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 c189dc78c8..c06304d6df 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.pgm</artifactId> diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties index fda0bf6ff4..b14531a1bd 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 @@ -58,9 +58,11 @@ couldNotCreateBranch=Could not create branch {0}: {1} dateInfo=Date: {0} deletedBranch=Deleted branch {0} deletedRemoteBranch=Deleted remote branch {0} -diffToolHelpSetToFollowing='git difftool --tool=<tool>' may be set to one of the following:\n{0}\n\tuser-defined:\n{1}\nThe following tools are valid, but not currently available:\n{2}\nSome of the tools listed above only work in a windowed\nenvironment. If run in a terminal-only session, they will fail. -diffToolLaunch=Viewing ({0}/{1}): '{2}'\nLaunch '{3}' [Y/n]? -diffToolDied=external diff died, stopping at {0} +diffToolHelpSetToFollowing=''git difftool --tool=<tool>'' may be set to one of the following:\n{0}\n\tuser-defined:\n{1}\nThe following tools are valid, but not currently available:\n{2}\nSome of the tools listed above only work in a windowed\nenvironment. If run in a terminal-only session, they will fail. +diffToolLaunch=Viewing ({0}/{1}): ''{2}''\nLaunch ''{3}'' [Y/n]? +diffToolDied=external diff died, stopping at path ''{0}'' due to exception: {1} +diffToolPromptToolName=This message is displayed because 'diff.tool' is not configured.\nSee 'git difftool --tool-help' or 'git help config' for more details.\n'git difftool' will now attempt to use one of the following tools:\n{0}\n +diffToolUnknownToolName=Unknown diff tool '{0}' doesNotExist={0} does not exist dontOverwriteLocalChanges=error: Your local changes to the following file would be overwritten by merge: everythingUpToDate=Everything up-to-date @@ -91,6 +93,24 @@ listeningOn=Listening on {0} logNoSignatureVerifier="No signature verifier available" mergeConflict=CONFLICT(content): Merge conflict in {0} mergeCheckoutConflict=error: Your local changes to the following files would be overwritten by merge: +mergeToolHelpSetToFollowing=''git mergetool --tool=<tool>'' may be set to one of the following:\n{0}\n\tuser-defined:\n{1}\nThe following tools are valid, but not currently available:\n{2}\nSome of the tools listed above only work in a windowed\nenvironment. If run in a terminal-only session, they will fail. +mergeToolLaunch=Hit return to start merge resolution tool ({0}): +mergeToolDied=local or remote cannot be found in cache, stopping at {0} +mergeToolNoFiles=No files need merging +mergeToolMerging=Merging:\n{0} +mergeToolUnknownConflict=\nUnknown merge conflict for ''{0}'': +mergeToolNormalConflict=\nNormal merge conflict for ''{0}'':\n '{'local'}': modified file\n '{'remote'}': modified file +mergeToolMergeFailed=merge of {0} failed +mergeToolExecutionError=excution error +mergeToolFileUnchanged=\n{0} seems unchanged. +mergeToolDeletedConflict=\nDeleted merge conflict for ''{0}'': +mergeToolDeletedConflictByUs= {local}: deleted\n {remote}: modified file +mergeToolDeletedConflictByThem= {local}: modified file\n {remote}: deleted +mergeToolContinueUnresolvedPaths=\nContinue merging other unresolved paths [y/n]? +mergeToolWasMergeSuccessfull=Was the merge successful [y/n]? +mergeToolDeletedMergeDecision=Use (m)odified or (d)eleted file, or (a)bort? +mergeToolPromptToolName=This message is displayed because 'merge.tool' is not configured.\nSee 'git mergetool --tool-help' or 'git help config' for more details.\n'git mergetool' will now attempt to use one of the following tools:\n{0}\n +mergeToolUnknownToolName=Unknown merge tool '{0}' mergeFailed=Automatic merge failed; fix conflicts and then commit the result mergeCheckoutFailed=Please, commit your changes or stash them before you can merge. mergeMadeBy=Merge made by the ''{0}'' strategy. @@ -255,6 +275,7 @@ usage_DisplayTheVersionOfJgit=Display the version of jgit usage_Gc=Cleanup unnecessary files and optimize the local repository usage_Glog=View commit history as a graph usage_DiffGuiTool=When git-difftool is invoked with the -g or --gui option the default diff tool will be read from the configured diff.guitool variable instead of diff.tool. +usage_MergeGuiTool=When git-mergetool is invoked with the -g or --gui option the default merge tool will be read from the configured merge.guitool variable instead of merge.tool. usage_noGui=The --no-gui option can be used to override -g or --gui setting. usage_IndexPack=Build pack index file for an existing packed archive usage_LFSDirectory=Directory to store large objects @@ -303,6 +324,7 @@ usage_Status=Show the working tree status usage_StopTrackingAFile=Stop tracking a file usage_TextHashFunctions=Scan repository to compute maximum number of collisions for hash functions usage_ToolForDiff=Use the diff tool specified by <tool>. Run git difftool --tool-help for the list of valid <tool> settings.\nIf a diff tool is not specified, git difftool will use the configuration variable diff.tool. +usage_ToolForMerge=Use the merge resolution program specified by <tool>. Run git mergetool --tool-help for the list of valid <tool> settings.\nIf a merge resolution program is not specified, git mergetool will use the configuration variable merge.tool. usage_UpdateRemoteRepositoryFromLocalRefs=Update remote repository from local refs usage_UseAll=Use all refs found in refs/ usage_UseTags=Use any tag including lightweight tags @@ -350,6 +372,7 @@ usage_date=date format, one of default, rfc, local, iso, short, raw (as defined usage_detectRenames=detect renamed files usage_diffAlgorithm=the diff algorithm to use. Currently supported are: 'myers', 'histogram' usage_DiffTool=git difftool is a Git command that allows you to compare and edit files between revisions using common diff tools.\ngit difftool is a frontend to git diff and accepts the same options and arguments. +usage_MergeTool=git-mergetool - Run merge conflict resolution tools to resolve merge conflicts.\nUse git mergetool to run one of several merge utilities to resolve merge conflicts. It is typically run after git merge. usage_directoriesToExport=directories to export usage_disableTheServiceInAllRepositories=disable the service in all repositories usage_displayAListOfAllRegisteredJgitCommands=Display a list of all registered jgit commands diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java index 128881779b..3e6042afee 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java @@ -1,5 +1,6 @@ /* - * Copyright (C) 2018-2021, Andre Bossert <andre.bossert@siemens.com> + * Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com> + * Copyright (C) 2019, Tim Neumann <tim.neumann@advantest.com> * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -11,25 +12,40 @@ package org.eclipse.jgit.pgm; import static org.eclipse.jgit.lib.Constants.HEAD; +import static org.eclipse.jgit.treewalk.TreeWalk.OperationType.CHECKOUT_OP; import java.io.BufferedOutputStream; import java.io.BufferedReader; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.text.MessageFormat; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.TimeUnit; +import org.eclipse.jgit.diff.ContentSource; +import org.eclipse.jgit.diff.ContentSource.Pair; import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.diff.DiffEntry.Side; import org.eclipse.jgit.diff.DiffFormatter; +import org.eclipse.jgit.dircache.DirCacheCheckout; +import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.AmbiguousObjectException; +import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.NoWorkTreeException; import org.eclipse.jgit.errors.RevisionSyntaxException; import org.eclipse.jgit.internal.diffmergetool.DiffTools; import org.eclipse.jgit.internal.diffmergetool.ExternalDiffTool; +import org.eclipse.jgit.internal.diffmergetool.FileElement; +import org.eclipse.jgit.internal.diffmergetool.PromptContinueHandler; +import org.eclipse.jgit.internal.diffmergetool.ToolException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; @@ -37,11 +53,16 @@ import org.eclipse.jgit.lib.TextProgressMonitor; import org.eclipse.jgit.lib.internal.BooleanTriState; import org.eclipse.jgit.pgm.internal.CLIText; import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler; +import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.AbstractTreeIterator; import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.WorkingTreeIterator; +import org.eclipse.jgit.treewalk.WorkingTreeOptions; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; import org.eclipse.jgit.treewalk.filter.TreeFilter; -import org.eclipse.jgit.util.StringUtils; +import org.eclipse.jgit.util.FS.ExecutionResult; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; @@ -57,9 +78,13 @@ class DiffTool extends TextBuiltin { @Argument(index = 1, metaVar = "metaVar_treeish") private AbstractTreeIterator newTree; + private Optional<String> toolName = Optional.empty(); + @Option(name = "--tool", aliases = { "-t" }, metaVar = "metaVar_tool", usage = "usage_ToolForDiff") - private String toolName; + void setToolName(String name) { + toolName = Optional.of(name); + } @Option(name = "--cached", aliases = { "--staged" }, usage = "usage_cached") private boolean cached; @@ -79,16 +104,16 @@ class DiffTool extends TextBuiltin { @Option(name = "--tool-help", usage = "usage_toolHelp") private boolean toolHelp; - private BooleanTriState gui = BooleanTriState.UNSET; + private boolean gui = false; @Option(name = "--gui", aliases = { "-g" }, usage = "usage_DiffGuiTool") void setGui(@SuppressWarnings("unused") boolean on) { - gui = BooleanTriState.TRUE; + gui = true; } @Option(name = "--no-gui", usage = "usage_noGui") void noGui(@SuppressWarnings("unused") boolean on) { - gui = BooleanTriState.FALSE; + gui = false; } private BooleanTriState trustExitCode = BooleanTriState.UNSET; @@ -106,11 +131,14 @@ class DiffTool extends TextBuiltin { @Option(name = "--", metaVar = "metaVar_paths", handler = PathTreeFilterHandler.class) private TreeFilter pathFilter = TreeFilter.ALL; + private BufferedReader inputReader; + @Override protected void init(Repository repository, String gitDir) { super.init(repository, gitDir); diffFmt = new DiffFormatter(new BufferedOutputStream(outs)); diffTools = new DiffTools(repository); + inputReader = new BufferedReader(new InputStreamReader(ins, StandardCharsets.UTF_8)); } @Override @@ -119,23 +147,12 @@ class DiffTool extends TextBuiltin { if (toolHelp) { showToolHelp(); } else { - boolean showPrompt = diffTools.isInteractive(); - if (prompt != BooleanTriState.UNSET) { - showPrompt = prompt == BooleanTriState.TRUE; - } - String toolNamePrompt = toolName; - if (showPrompt) { - if (StringUtils.isEmptyOrNull(toolNamePrompt)) { - toolNamePrompt = diffTools.getDefaultToolName(gui); - } - } // get the changed files List<DiffEntry> files = getFiles(); if (files.size() > 0) { - compare(files, showPrompt, toolNamePrompt); + compare(files); } } - outw.flush(); } catch (RevisionSyntaxException | IOException e) { throw die(e.getMessage(), e); } finally { @@ -143,77 +160,127 @@ class DiffTool extends TextBuiltin { } } - private void compare(List<DiffEntry> files, boolean showPrompt, - String toolNamePrompt) throws IOException { - for (int fileIndex = 0; fileIndex < files.size(); fileIndex++) { - DiffEntry ent = files.get(fileIndex); - String mergedFilePath = ent.getNewPath(); - if (mergedFilePath.equals(DiffEntry.DEV_NULL)) { - mergedFilePath = ent.getOldPath(); - } - // check if user wants to launch compare - boolean launchCompare = true; - if (showPrompt) { - launchCompare = isLaunchCompare(fileIndex + 1, files.size(), - mergedFilePath, toolNamePrompt); + private void informUserNoTool(List<String> tools) { + try { + StringBuilder toolNames = new StringBuilder(); + for (String name : tools) { + toolNames.append(name + " "); //$NON-NLS-1$ } - if (launchCompare) { - switch (ent.getChangeType()) { - case MODIFY: - outw.println("M\t" + ent.getNewPath() //$NON-NLS-1$ - + " (" + ent.getNewId().name() + ")" //$NON-NLS-1$ //$NON-NLS-2$ - + "\t" + ent.getOldPath() //$NON-NLS-1$ - + " (" + ent.getOldId().name() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ - int ret = diffTools.compare(ent.getNewPath(), - ent.getOldPath(), ent.getNewId().name(), - ent.getOldId().name(), toolName, prompt, gui, - trustExitCode); - if (ret != 0) { - throw die(MessageFormat.format( - CLIText.get().diffToolDied, mergedFilePath)); + outw.println(MessageFormat.format( + CLIText.get().diffToolPromptToolName, toolNames)); + outw.flush(); + } catch (IOException e) { + throw new IllegalStateException("Cannot output text", e); //$NON-NLS-1$ + } + } + + private class CountingPromptContinueHandler + implements PromptContinueHandler { + private final int fileIndex; + + private final int fileCount; + + private final String fileName; + + public CountingPromptContinueHandler(int fileIndex, int fileCount, + String fileName) { + this.fileIndex = fileIndex; + this.fileCount = fileCount; + this.fileName = fileName; + } + + @SuppressWarnings("boxing") + @Override + public boolean prompt(String toolToLaunchName) { + try { + boolean launchCompare = true; + outw.println(MessageFormat.format(CLIText.get().diffToolLaunch, + fileIndex, fileCount, fileName, toolToLaunchName) + + " "); //$NON-NLS-1$ + outw.flush(); + BufferedReader br = inputReader; + String line = null; + if ((line = br.readLine()) != null) { + if (!line.equalsIgnoreCase("Y")) { //$NON-NLS-1$ + launchCompare = false; } - break; - default: - break; } - } else { - break; + return launchCompare; + } catch (IOException e) { + throw new IllegalStateException("Cannot output text", e); //$NON-NLS-1$ } } } - @SuppressWarnings("boxing") - private boolean isLaunchCompare(int fileIndex, int fileCount, - String fileName, String toolNamePrompt) throws IOException { - boolean launchCompare = true; - outw.println(MessageFormat.format(CLIText.get().diffToolLaunch, - fileIndex, fileCount, fileName, toolNamePrompt)); - outw.flush(); - BufferedReader br = new BufferedReader( - new InputStreamReader(ins, StandardCharsets.UTF_8)); - String line = null; - if ((line = br.readLine()) != null) { - if (!line.equalsIgnoreCase("Y")) { //$NON-NLS-1$ - launchCompare = false; + private void compare(List<DiffEntry> files) throws IOException { + ContentSource.Pair sourcePair = new ContentSource.Pair(source(oldTree), + source(newTree)); + try { + for (int fileIndex = 0; fileIndex < files.size(); fileIndex++) { + DiffEntry ent = files.get(fileIndex); + + String filePath = ent.getNewPath(); + if (filePath.equals(DiffEntry.DEV_NULL)) { + filePath = ent.getOldPath(); + } + + try { + FileElement local = createFileElement( + FileElement.Type.LOCAL, sourcePair, Side.OLD, ent); + FileElement remote = createFileElement( + FileElement.Type.REMOTE, sourcePair, Side.NEW, ent); + + PromptContinueHandler promptContinueHandler = new CountingPromptContinueHandler( + fileIndex + 1, files.size(), filePath); + + Optional<ExecutionResult> optionalResult = diffTools + .compare(local, remote, toolName, prompt, gui, + trustExitCode, promptContinueHandler, + this::informUserNoTool); + + if (optionalResult.isPresent()) { + ExecutionResult result = optionalResult.get(); + // TODO: check how to return the exit-code of the tool + // to jgit / java runtime ? + // int rc =... + outw.println( + new String(result.getStdout().toByteArray())); + outw.flush(); + errw.println( + new String(result.getStderr().toByteArray())); + errw.flush(); + } + } catch (ToolException e) { + outw.println(e.getResultStdout()); + outw.flush(); + errw.println(e.getMessage()); + errw.flush(); + throw die(MessageFormat.format( + CLIText.get().diffToolDied, filePath, e), e); + } } + } finally { + sourcePair.close(); } - return launchCompare; } private void showToolHelp() throws IOException { + Map<String, ExternalDiffTool> predefTools = diffTools + .getPredefinedTools(true); StringBuilder availableToolNames = new StringBuilder(); - for (String name : diffTools.getAvailableTools().keySet()) { - availableToolNames.append(String.format("\t\t%s\n", name)); //$NON-NLS-1$ - } StringBuilder notAvailableToolNames = new StringBuilder(); - for (String name : diffTools.getNotAvailableTools().keySet()) { - notAvailableToolNames.append(String.format("\t\t%s\n", name)); //$NON-NLS-1$ + for (String name : predefTools.keySet()) { + if (predefTools.get(name).isAvailable()) { + availableToolNames.append(MessageFormat.format("\t\t{0}\n", name)); //$NON-NLS-1$ + } else { + notAvailableToolNames.append(MessageFormat.format("\t\t{0}\n", name)); //$NON-NLS-1$ + } } StringBuilder userToolNames = new StringBuilder(); Map<String, ExternalDiffTool> userTools = diffTools .getUserDefinedTools(); for (String name : userTools.keySet()) { - userToolNames.append(String.format("\t\t%s.cmd %s\n", //$NON-NLS-1$ + userToolNames.append(MessageFormat.format("\t\t{0}.cmd {1}\n", //$NON-NLS-1$ name, userTools.get(name).getCommand())); } outw.println(MessageFormat.format( @@ -254,4 +321,54 @@ class DiffTool extends TextBuiltin { return files; } + private FileElement createFileElement(FileElement.Type elementType, + Pair pair, Side side, DiffEntry entry) throws NoWorkTreeException, + CorruptObjectException, IOException, ToolException { + String entryPath = side == Side.NEW ? entry.getNewPath() + : entry.getOldPath(); + FileElement fileElement = new FileElement(entryPath, elementType, + db.getWorkTree()); + if (!pair.isWorkingTreeSource(side) && !fileElement.isNullPath()) { + try (RevWalk revWalk = new RevWalk(db); + TreeWalk treeWalk = new TreeWalk(db, + revWalk.getObjectReader())) { + treeWalk.setFilter( + PathFilterGroup.createFromStrings(entryPath)); + if (side == Side.NEW) { + newTree.reset(); + treeWalk.addTree(newTree); + } else { + oldTree.reset(); + treeWalk.addTree(oldTree); + } + if (treeWalk.next()) { + final EolStreamType eolStreamType = treeWalk + .getEolStreamType(CHECKOUT_OP); + final String filterCommand = treeWalk.getFilterCommand( + Constants.ATTR_FILTER_TYPE_SMUDGE); + WorkingTreeOptions opt = db.getConfig() + .get(WorkingTreeOptions.KEY); + CheckoutMetadata checkoutMetadata = new CheckoutMetadata( + eolStreamType, filterCommand); + DirCacheCheckout.getContent(db, entryPath, + checkoutMetadata, pair.open(side, entry), opt, + new FileOutputStream( + fileElement.createTempFile(null))); + } else { + throw new ToolException("Cannot find path '" + entryPath //$NON-NLS-1$ + + "' in staging area!", //$NON-NLS-1$ + null); + } + } + } + return fileElement; + } + + private ContentSource source(AbstractTreeIterator iterator) { + if (iterator instanceof WorkingTreeIterator) { + return ContentSource.create((WorkingTreeIterator) iterator); + } + return ContentSource.create(db.newObjectReader()); + } + } diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeTool.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeTool.java new file mode 100644 index 0000000000..2a411b81fe --- /dev/null +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeTool.java @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com> + * Copyright (C) 2019, Tim Neumann <tim.neumann@advantest.com> + * + * 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.eclipse.jgit.treewalk.TreeWalk.OperationType.CHECKOUT_OP; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.Status; +import org.eclipse.jgit.api.StatusCommand; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.diff.ContentSource; +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheCheckout; +import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.dircache.DirCacheIterator; +import org.eclipse.jgit.errors.NoWorkTreeException; +import org.eclipse.jgit.errors.RevisionSyntaxException; +import org.eclipse.jgit.internal.diffmergetool.ExternalMergeTool; +import org.eclipse.jgit.internal.diffmergetool.FileElement; +import org.eclipse.jgit.internal.diffmergetool.FileElement.Type; +import org.eclipse.jgit.internal.diffmergetool.MergeTools; +import org.eclipse.jgit.internal.diffmergetool.ToolException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; +import org.eclipse.jgit.lib.IndexDiff.StageState; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.internal.BooleanTriState; +import org.eclipse.jgit.pgm.internal.CLIText; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.WorkingTreeOptions; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; +import org.eclipse.jgit.util.FS.ExecutionResult; +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.Option; +import org.kohsuke.args4j.spi.RestOfArgumentsHandler; + +@Command(name = "mergetool", common = true, usage = "usage_MergeTool") +class MergeTool extends TextBuiltin { + private MergeTools mergeTools; + + private Optional<String> toolName = Optional.empty(); + + @Option(name = "--tool", aliases = { + "-t" }, metaVar = "metaVar_tool", usage = "usage_ToolForMerge") + void setToolName(String name) { + toolName = Optional.of(name); + } + + private BooleanTriState prompt = BooleanTriState.UNSET; + + @Option(name = "--prompt", usage = "usage_prompt") + void setPrompt(@SuppressWarnings("unused") boolean on) { + prompt = BooleanTriState.TRUE; + } + + @Option(name = "--no-prompt", aliases = { "-y" }, usage = "usage_noPrompt") + void noPrompt(@SuppressWarnings("unused") boolean on) { + prompt = BooleanTriState.FALSE; + } + + @Option(name = "--tool-help", usage = "usage_toolHelp") + private boolean toolHelp; + + private boolean gui = false; + + @Option(name = "--gui", aliases = { "-g" }, usage = "usage_MergeGuiTool") + void setGui(@SuppressWarnings("unused") boolean on) { + gui = true; + } + + @Option(name = "--no-gui", usage = "usage_noGui") + void noGui(@SuppressWarnings("unused") boolean on) { + gui = false; + } + + @Argument(required = false, index = 0, metaVar = "metaVar_paths") + @Option(name = "--", metaVar = "metaVar_paths", handler = RestOfArgumentsHandler.class) + protected List<String> filterPaths; + + private BufferedReader inputReader; + + @Override + protected void init(Repository repository, String gitDir) { + super.init(repository, gitDir); + mergeTools = new MergeTools(repository); + inputReader = new BufferedReader(new InputStreamReader(ins)); + } + + enum MergeResult { + SUCCESSFUL, FAILED, ABORTED + } + + @Override + protected void run() { + try { + if (toolHelp) { + showToolHelp(); + } else { + // get the changed files + Map<String, StageState> files = getFiles(); + if (files.size() > 0) { + merge(files); + } else { + outw.println(CLIText.get().mergeToolNoFiles); + } + } + outw.flush(); + } catch (Exception e) { + throw die(e.getMessage(), e); + } + } + + private void informUserNoTool(List<String> tools) { + try { + StringBuilder toolNames = new StringBuilder(); + for (String name : tools) { + toolNames.append(name + " "); //$NON-NLS-1$ + } + outw.println(MessageFormat + .format(CLIText.get().mergeToolPromptToolName, toolNames)); + outw.flush(); + } catch (IOException e) { + throw new IllegalStateException("Cannot output text", e); //$NON-NLS-1$ + } + } + + private void merge(Map<String, StageState> files) throws Exception { + // sort file names + List<String> mergedFilePaths = new ArrayList<>(files.keySet()); + Collections.sort(mergedFilePaths); + // show the files + StringBuilder mergedFiles = new StringBuilder(); + for (String mergedFilePath : mergedFilePaths) { + mergedFiles.append(MessageFormat.format("{0}\n", mergedFilePath)); //$NON-NLS-1$ + } + outw.println(MessageFormat.format(CLIText.get().mergeToolMerging, + mergedFiles)); + outw.flush(); + boolean showPrompt = mergeTools.isInteractive(); + if (prompt != BooleanTriState.UNSET) { + showPrompt = prompt == BooleanTriState.TRUE; + } + // merge the files + MergeResult mergeResult = MergeResult.SUCCESSFUL; + for (String mergedFilePath : mergedFilePaths) { + // if last merge failed... + if (mergeResult == MergeResult.FAILED) { + // check if user wants to continue + if (showPrompt && !isContinueUnresolvedPaths()) { + mergeResult = MergeResult.ABORTED; + } + } + // aborted ? + if (mergeResult == MergeResult.ABORTED) { + break; + } + // get file stage state and merge + StageState fileState = files.get(mergedFilePath); + if (fileState == StageState.BOTH_MODIFIED) { + mergeResult = mergeModified(mergedFilePath, showPrompt); + } else if ((fileState == StageState.DELETED_BY_US) + || (fileState == StageState.DELETED_BY_THEM)) { + mergeResult = mergeDeleted(mergedFilePath, + fileState == StageState.DELETED_BY_US); + } else { + outw.println(MessageFormat.format( + CLIText.get().mergeToolUnknownConflict, + mergedFilePath)); + mergeResult = MergeResult.ABORTED; + } + } + } + + private MergeResult mergeModified(String mergedFilePath, boolean showPrompt) + throws Exception { + outw.println(MessageFormat.format(CLIText.get().mergeToolNormalConflict, + mergedFilePath)); + outw.flush(); + boolean isMergeSuccessful = true; + ContentSource baseSource = ContentSource.create(db.newObjectReader()); + ContentSource localSource = ContentSource.create(db.newObjectReader()); + ContentSource remoteSource = ContentSource.create(db.newObjectReader()); + // temporary directory if mergetool.writeToTemp == true + File tempDir = mergeTools.createTempDirectory(); + // the parent directory for temp files (can be same as tempDir or just + // the worktree dir) + File tempFilesParent = tempDir != null ? tempDir : db.getWorkTree(); + try { + FileElement base = null; + FileElement local = null; + FileElement remote = null; + FileElement merged = new FileElement(mergedFilePath, Type.MERGED, + db.getWorkTree()); + DirCache cache = db.readDirCache(); + try (RevWalk revWalk = new RevWalk(db); + TreeWalk treeWalk = new TreeWalk(db, + revWalk.getObjectReader())) { + treeWalk.setFilter( + PathFilterGroup.createFromStrings(mergedFilePath)); + DirCacheIterator cacheIter = new DirCacheIterator(cache); + treeWalk.addTree(cacheIter); + while (treeWalk.next()) { + if (treeWalk.isSubtree()) { + treeWalk.enterSubtree(); + continue; + } + final EolStreamType eolStreamType = treeWalk + .getEolStreamType(CHECKOUT_OP); + final String filterCommand = treeWalk.getFilterCommand( + Constants.ATTR_FILTER_TYPE_SMUDGE); + WorkingTreeOptions opt = db.getConfig() + .get(WorkingTreeOptions.KEY); + CheckoutMetadata checkoutMetadata = new CheckoutMetadata( + eolStreamType, filterCommand); + DirCacheEntry entry = treeWalk + .getTree(DirCacheIterator.class).getDirCacheEntry(); + if (entry == null) { + continue; + } + ObjectId id = entry.getObjectId(); + switch (entry.getStage()) { + case DirCacheEntry.STAGE_1: + base = new FileElement(mergedFilePath, Type.BASE); + DirCacheCheckout.getContent(db, mergedFilePath, + checkoutMetadata, + baseSource.open(mergedFilePath, id), opt, + new FileOutputStream( + base.createTempFile(tempFilesParent))); + break; + case DirCacheEntry.STAGE_2: + local = new FileElement(mergedFilePath, Type.LOCAL); + DirCacheCheckout.getContent(db, mergedFilePath, + checkoutMetadata, + localSource.open(mergedFilePath, id), opt, + new FileOutputStream( + local.createTempFile(tempFilesParent))); + break; + case DirCacheEntry.STAGE_3: + remote = new FileElement(mergedFilePath, Type.REMOTE); + DirCacheCheckout.getContent(db, mergedFilePath, + checkoutMetadata, + remoteSource.open(mergedFilePath, id), opt, + new FileOutputStream(remote + .createTempFile(tempFilesParent))); + break; + } + } + } + if ((local == null) || (remote == null)) { + throw die(MessageFormat.format(CLIText.get().mergeToolDied, + mergedFilePath)); + } + long modifiedBefore = merged.getFile().lastModified(); + try { + // TODO: check how to return the exit-code of the + // tool to jgit / java runtime ? + // int rc =... + Optional<ExecutionResult> optionalResult = mergeTools.merge( + local, remote, merged, base, tempDir, toolName, prompt, + gui, this::promptForLaunch, this::informUserNoTool); + if (optionalResult.isPresent()) { + ExecutionResult result = optionalResult.get(); + outw.println(new String(result.getStdout().toByteArray())); + outw.flush(); + errw.println(new String(result.getStderr().toByteArray())); + errw.flush(); + } else { + return MergeResult.ABORTED; + } + } catch (ToolException e) { + isMergeSuccessful = false; + outw.println(e.getResultStdout()); + outw.flush(); + errw.println(e.getMessage()); + errw.println(MessageFormat.format( + CLIText.get().mergeToolMergeFailed, mergedFilePath)); + errw.flush(); + if (e.isCommandExecutionError()) { + throw die(CLIText.get().mergeToolExecutionError, e); + } + } + // if merge was successful check file modified + if (isMergeSuccessful) { + long modifiedAfter = merged.getFile().lastModified(); + if (modifiedBefore == modifiedAfter) { + outw.println(MessageFormat.format( + CLIText.get().mergeToolFileUnchanged, + mergedFilePath)); + isMergeSuccessful = !showPrompt || isMergeSuccessful(); + } + } + // if automatically or manually successful + // -> add the file to the index + if (isMergeSuccessful) { + addFile(mergedFilePath); + } + } finally { + baseSource.close(); + localSource.close(); + remoteSource.close(); + } + return isMergeSuccessful ? MergeResult.SUCCESSFUL : MergeResult.FAILED; + } + + private MergeResult mergeDeleted(String mergedFilePath, boolean deletedByUs) + throws Exception { + outw.println(MessageFormat.format(CLIText.get().mergeToolFileUnchanged, + mergedFilePath)); + if (deletedByUs) { + outw.println(CLIText.get().mergeToolDeletedConflictByUs); + } else { + outw.println(CLIText.get().mergeToolDeletedConflictByThem); + } + int mergeDecision = getDeletedMergeDecision(); + if (mergeDecision == 1) { + // add modified file + addFile(mergedFilePath); + } else if (mergeDecision == -1) { + // remove deleted file + rmFile(mergedFilePath); + } else { + return MergeResult.ABORTED; + } + return MergeResult.SUCCESSFUL; + } + + private void addFile(String fileName) throws Exception { + try (Git git = new Git(db)) { + git.add().addFilepattern(fileName).call(); + } + } + + private void rmFile(String fileName) throws Exception { + try (Git git = new Git(db)) { + git.rm().addFilepattern(fileName).call(); + } + } + + private boolean hasUserAccepted(String message) throws IOException { + boolean yes = true; + outw.print(message + " "); //$NON-NLS-1$ + outw.flush(); + BufferedReader br = inputReader; + String line = null; + while ((line = br.readLine()) != null) { + if (line.equalsIgnoreCase("y")) { //$NON-NLS-1$ + yes = true; + break; + } else if (line.equalsIgnoreCase("n")) { //$NON-NLS-1$ + yes = false; + break; + } + outw.print(message); + outw.flush(); + } + return yes; + } + + private boolean isContinueUnresolvedPaths() throws IOException { + return hasUserAccepted(CLIText.get().mergeToolContinueUnresolvedPaths); + } + + private boolean isMergeSuccessful() throws IOException { + return hasUserAccepted(CLIText.get().mergeToolWasMergeSuccessfull); + } + + private boolean promptForLaunch(String toolNamePrompt) { + try { + boolean launch = true; + outw.print(MessageFormat.format(CLIText.get().mergeToolLaunch, + toolNamePrompt) + " "); //$NON-NLS-1$ + outw.flush(); + BufferedReader br = inputReader; + String line = null; + if ((line = br.readLine()) != null) { + if (!line.equalsIgnoreCase("y") && !line.equalsIgnoreCase("")) { //$NON-NLS-1$ //$NON-NLS-2$ + launch = false; + } + } + return launch; + } catch (IOException e) { + throw new IllegalStateException("Cannot output text", e); //$NON-NLS-1$ + } + } + + private int getDeletedMergeDecision() throws IOException { + int ret = 0; // abort + final String message = CLIText.get().mergeToolDeletedMergeDecision + + " "; //$NON-NLS-1$ + outw.print(message); + outw.flush(); + BufferedReader br = inputReader; + String line = null; + while ((line = br.readLine()) != null) { + if (line.equalsIgnoreCase("m")) { //$NON-NLS-1$ + ret = 1; // modified + break; + } else if (line.equalsIgnoreCase("d")) { //$NON-NLS-1$ + ret = -1; // deleted + break; + } else if (line.equalsIgnoreCase("a")) { //$NON-NLS-1$ + break; + } + outw.print(message); + outw.flush(); + } + return ret; + } + + private void showToolHelp() throws IOException { + Map<String, ExternalMergeTool> predefTools = mergeTools + .getPredefinedTools(true); + StringBuilder availableToolNames = new StringBuilder(); + StringBuilder notAvailableToolNames = new StringBuilder(); + for (String name : predefTools.keySet()) { + if (predefTools.get(name).isAvailable()) { + availableToolNames.append(MessageFormat.format("\t\t{0}\n", name)); //$NON-NLS-1$ + } else { + notAvailableToolNames.append(MessageFormat.format("\t\t{0}\n", name)); //$NON-NLS-1$ + } + } + StringBuilder userToolNames = new StringBuilder(); + Map<String, ExternalMergeTool> userTools = mergeTools + .getUserDefinedTools(); + for (String name : userTools.keySet()) { + userToolNames.append(MessageFormat.format("\t\t{0}.cmd {1}\n", //$NON-NLS-1$ + name, userTools.get(name).getCommand())); + } + outw.println(MessageFormat.format( + CLIText.get().mergeToolHelpSetToFollowing, availableToolNames, + userToolNames, notAvailableToolNames)); + } + + private Map<String, StageState> getFiles() throws RevisionSyntaxException, + NoWorkTreeException, GitAPIException { + Map<String, StageState> files = new TreeMap<>(); + try (Git git = new Git(db)) { + StatusCommand statusCommand = git.status(); + if (filterPaths != null && filterPaths.size() > 0) { + for (String path : filterPaths) { + statusCommand.addPath(path); + } + } + Status status = statusCommand.call(); + files = status.getConflictingStageState(); + } + return files; + } + +} diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java index 7fe5b0fa45..e06f150e51 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java @@ -139,6 +139,8 @@ public class CLIText extends TranslationBundle { /***/ public String diffToolHelpSetToFollowing; /***/ public String diffToolLaunch; /***/ public String diffToolDied; + /***/ public String diffToolPromptToolName; + /***/ public String diffToolUnknownToolName; /***/ public String doesNotExist; /***/ public String dontOverwriteLocalChanges; /***/ public String everythingUpToDate; @@ -169,6 +171,24 @@ public class CLIText extends TranslationBundle { /***/ public String logNoSignatureVerifier; /***/ public String mergeCheckoutConflict; /***/ public String mergeConflict; + /***/ public String mergeToolHelpSetToFollowing; + /***/ public String mergeToolLaunch; + /***/ public String mergeToolDied; + /***/ public String mergeToolNoFiles; + /***/ public String mergeToolMerging; + /***/ public String mergeToolUnknownConflict; + /***/ public String mergeToolNormalConflict; + /***/ public String mergeToolMergeFailed; + /***/ public String mergeToolExecutionError; + /***/ public String mergeToolFileUnchanged; + /***/ public String mergeToolDeletedConflict; + /***/ public String mergeToolDeletedConflictByUs; + /***/ public String mergeToolDeletedConflictByThem; + /***/ public String mergeToolContinueUnresolvedPaths; + /***/ public String mergeToolWasMergeSuccessfull; + /***/ public String mergeToolDeletedMergeDecision; + /***/ public String mergeToolPromptToolName; + /***/ public String mergeToolUnknownToolName; /***/ public String mergeFailed; /***/ public String mergeCheckoutFailed; /***/ public String mergeMadeBy; 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 1a5746df00..64f6861bd8 100644 --- a/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF @@ -2,15 +2,16 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent;singleton:=true -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier +Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor -Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[6.1.1,6.2.0)" +Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[6.2.0,6.3.0)" Bundle-ActivationPolicy: lazy Automatic-Module-Name: org.eclipse.jgit.ssh.apache.agent Bundle-RequiredExecutionEnvironment: JavaSE-11 -Import-Package: org.eclipse.jgit.transport.sshd;version="[6.1.1,6.2.0)", - org.eclipse.jgit.nls;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)" +Import-Package: org.eclipse.jgit.transport.sshd;version="[6.2.0,6.3.0)", + org.eclipse.jgit.nls;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.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="6.1.1";x-internal:=true +Export-Package: org.eclipse.jgit.internal.transport.sshd.agent.connector;version="6.2.0";x-internal:=true diff --git a/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF index d06e6b172f..57f00a94e6 100644 --- a/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.ssh.apache.agent - Sources Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.ssh.apache.agent/pom.xml b/org.eclipse.jgit.ssh.apache.agent/pom.xml index 77b9c952fb..262b9c6476 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ssh.apache.agent</artifactId> diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/WinPipeConnector.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/WinPipeConnector.java index 7bad90f24f..81c653722f 100644 --- a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/WinPipeConnector.java +++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/WinPipeConnector.java @@ -90,7 +90,7 @@ public class WinPipeConnector extends AbstractConnector { file = libs.kernel.CreateFile(pipeName, WinNT.GENERIC_READ | WinNT.GENERIC_WRITE, FILE_SHARE_NONE, null, WinNT.OPEN_EXISTING, FILE_ATTRIBUTE_NONE, null); - if (file == null || file == WinBase.INVALID_HANDLE_VALUE) { + if (file == null || WinBase.INVALID_HANDLE_VALUE.equals(file)) { int errorCode = libs.kernel.GetLastError(); if (errorCode == WinError.ERROR_FILE_NOT_FOUND && CANONICAL_PIPE_NAME.equalsIgnoreCase(pipeName)) { 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 411d43cf8a..794a585731 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-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.ssh.apache.test Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.test -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-11 @@ -21,16 +21,16 @@ Import-Package: org.apache.sshd.client.config.hosts;version="[2.8.0,2.9.0)", org.apache.sshd.core;version="[2.8.0,2.9.0)", org.apache.sshd.server;version="[2.8.0,2.9.0)", org.apache.sshd.server.forward;version="[2.8.0,2.9.0)", - org.eclipse.jgit.api;version="[6.1.1,6.2.0)", - org.eclipse.jgit.api.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.transport.sshd.proxy;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit.ssh;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.sshd;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.sshd.agent;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", + org.eclipse.jgit.api;version="[6.2.0,6.3.0)", + org.eclipse.jgit.api.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.transport.sshd.proxy;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit.ssh;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.sshd;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.sshd.agent;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.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.apache.test/pom.xml b/org.eclipse.jgit.ssh.apache.test/pom.xml index 3e98e33371..6f9dcac175 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId> diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java index 3d7c7651c1..a8fcca7b8e 100644 --- a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java +++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -789,4 +789,76 @@ public class ApacheSshTest extends SshTestBase { session.disconnect(); } } + + private void verifyAuthLog(String message, String first) { + assertTrue(message.contains(System.lineSeparator())); + String[] lines = message.split(System.lineSeparator()); + int pubkeyIndex = -1; + int passwordIndex = -1; + for (int i = 0; i < lines.length; i++) { + String line = lines[i]; + if (i == 0) { + assertTrue(line.contains(first)); + } + if (line.contains("publickey:")) { + if (pubkeyIndex < 0) { + pubkeyIndex = i; + assertTrue(line.contains("/userkey")); + } + } else if (line.contains("password:")) { + if (passwordIndex < 0) { + passwordIndex = i; + assertTrue(line.contains("attempt 1")); + } + } + } + assertTrue(pubkeyIndex > 0 && passwordIndex > 0); + assertTrue(pubkeyIndex < passwordIndex); + } + + @Test + public void testAuthFailureMessageCancel() throws Exception { + File userKey = new File(getTemporaryDirectory(), "userkey"); + copyTestResource("id_ed25519", userKey); + File publicKey = new File(getTemporaryDirectory(), "userkey.pub"); + copyTestResource("id_ed25519.pub", publicKey); + // Don't set this as the user's key; we do want to try with a wrong key. + server.enablePasswordAuthentication(); + TestCredentialsProvider provider = new TestCredentialsProvider( + "wrongpass"); + TransportException e = assertThrows(TransportException.class, + () -> cloneWith("ssh://git/doesntmatter", defaultCloneDir, + provider, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + userKey.getAbsolutePath(), // + "PreferredAuthentications publickey,password")); + verifyAuthLog(e.getMessage(), "canceled"); + } + + @Test + public void testAuthFailureMessage() throws Exception { + File userKey = new File(getTemporaryDirectory(), "userkey"); + copyTestResource("id_ed25519", userKey); + File publicKey = new File(getTemporaryDirectory(), "userkey.pub"); + copyTestResource("id_ed25519.pub", publicKey); + // Don't set this as the user's key; we do want to try with a wrong key. + server.enablePasswordAuthentication(); + // Enough passwords not to cancel authentication + TestCredentialsProvider provider = new TestCredentialsProvider( + "wrongpass", "wrongpass", "wrongpass"); + TransportException e = assertThrows(TransportException.class, + () -> cloneWith("ssh://git/doesntmatter", defaultCloneDir, + provider, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + userKey.getAbsolutePath(), // + "PreferredAuthentications publickey,password")); + verifyAuthLog(e.getMessage(), "log in"); + } + } diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF index 73feaeca85..041f0ce987 100644 --- a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF @@ -6,9 +6,9 @@ Bundle-SymbolicName: org.eclipse.jgit.ssh.apache Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-ActivationPolicy: lazy -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 -Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.1.1";x-internal:=true; +Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.2.0";x-internal:=true; uses:="org.apache.sshd.client, org.apache.sshd.client.auth, org.apache.sshd.client.auth.keyboard, @@ -23,17 +23,17 @@ Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.1.1";x-inter org.apache.sshd.common.signature, org.apache.sshd.common.util.buffer, org.eclipse.jgit.transport", - org.eclipse.jgit.internal.transport.sshd.agent;version="6.1.1";x-internal:=true, - org.eclipse.jgit.internal.transport.sshd.auth;version="6.1.1";x-internal:=true, - org.eclipse.jgit.internal.transport.sshd.proxy;version="6.1.1";x-friends:="org.eclipse.jgit.ssh.apache.test", - org.eclipse.jgit.transport.sshd;version="6.1.1"; + org.eclipse.jgit.internal.transport.sshd.agent;version="6.2.0";x-internal:=true, + org.eclipse.jgit.internal.transport.sshd.auth;version="6.2.0";x-internal:=true, + org.eclipse.jgit.internal.transport.sshd.proxy;version="6.2.0";x-friends:="org.eclipse.jgit.ssh.apache.test", + org.eclipse.jgit.transport.sshd;version="6.2.0"; uses:="org.eclipse.jgit.transport, org.apache.sshd.client.config.hosts, org.apache.sshd.common.keyprovider, org.eclipse.jgit.util, org.apache.sshd.client.session, org.apache.sshd.client.keyverifier", - org.eclipse.jgit.transport.sshd.agent;version="6.1.1" + org.eclipse.jgit.transport.sshd.agent;version="6.2.0" Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)", org.apache.sshd.agent;version="[2.8.0,2.9.0)", org.apache.sshd.client;version="[2.8.0,2.9.0)", @@ -86,12 +86,12 @@ Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)", org.apache.sshd.sftp;version="[2.8.0,2.9.0)", org.apache.sshd.sftp.client;version="[2.8.0,2.9.0)", org.apache.sshd.sftp.common;version="[2.8.0,2.9.0)", - org.eclipse.jgit.annotations;version="[6.1.1,6.2.0)", - org.eclipse.jgit.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.fnmatch;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.transport.ssh;version="[6.1.1,6.2.0)", - org.eclipse.jgit.nls;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", + org.eclipse.jgit.annotations;version="[6.2.0,6.3.0)", + org.eclipse.jgit.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.fnmatch;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.transport.ssh;version="[6.2.0,6.3.0)", + org.eclipse.jgit.nls;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.0)", org.slf4j;version="[1.7.0,2.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 9fefa6e74c..76a9fd5ab6 100644 --- a/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.ssh.apache - Sources Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml index 674c7a49e5..106bc38e3c 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ssh.apache</artifactId> @@ -50,16 +50,6 @@ <groupId>org.apache.sshd</groupId> <artifactId>sshd-sftp</artifactId> <version>${apache-sshd-version}</version> - <exclusions> - <exclusion> - <groupId>org.apache.sshd</groupId> - <artifactId>sshd-core</artifactId> - </exclusion> - <exclusion> - <groupId>org.apache.sshd</groupId> - <artifactId>sshd-common</artifactId> - </exclusion> - </exclusions> </dependency> <dependency> 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 4f735bab34..c676221800 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 @@ -1,5 +1,22 @@ authenticationCanceled=SSH authentication canceled: no password given authenticationOnClosedSession=Authentication canceled: session is already closing or closed +authGssApiAttempt={0}: trying mechanism OID {1} +authGssApiExhausted={0}: no more mechanisms to try +authGssApiFailure={0}: server refused authentication; mechanism {1} +authGssApiNotTried={0}: not tried +authGssApiPartialSuccess={0}: partial success with mechanism OID {1}, continue with authentication methods {2} +authPasswordAttempt={0}: attempt {1} +authPasswordChangeAttempt={0}: attempt {1} with password change +authPasswordExhausted={0}: no more attempts +authPasswordFailure={0}: server refused (wrong password) +authPasswordNotTried={0}: not tried +authPasswordPartialSuccess={0}: partial success, continue with authentication methods {1} +authPubkeyAttempt={0}: trying {1} key {2} with signature type {3} +authPubkeyAttemptAgent={0}: trying {1} key {2} from SSH agent with signature type {3} +authPubkeyExhausted={0}: no more keys to try +authPubkeyFailure={0}: server refused {1} key {2} +authPubkeyNoKeys={0}: no keys to try +authPubkeyPartialSuccess={0}: partial success for {1} key {2}, continue with authentication methods {3} cannotReadPublicKey=Cannot read public key from file {0} closeListenerFailed=Ssh session close listener failed configInvalidPath=Invalid path in ssh config key {0}: {1} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/AuthenticationLogger.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/AuthenticationLogger.java new file mode 100644 index 0000000000..add79b35c9 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/AuthenticationLogger.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.internal.transport.sshd; + +import static org.eclipse.jgit.internal.transport.sshd.CachingKeyPairProvider.getKeyId; + +import java.security.KeyPair; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.sshd.client.auth.password.PasswordAuthenticationReporter; +import org.apache.sshd.client.auth.password.UserAuthPassword; +import org.apache.sshd.client.auth.pubkey.PublicKeyAuthenticationReporter; +import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey; +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.config.keys.KeyUtils; + +/** + * Provides a log of authentication attempts for a {@link ClientSession}. + */ +public class AuthenticationLogger { + + private final List<String> messages = new ArrayList<>(); + + // We're interested in this log only in the failure case, so we don't need + // to log authentication success. + + private final PublicKeyAuthenticationReporter pubkeyLogger = new PublicKeyAuthenticationReporter() { + + private boolean hasAttempts; + + @Override + public void signalAuthenticationAttempt(ClientSession session, + String service, KeyPair identity, String signature) + throws Exception { + hasAttempts = true; + String message; + if (identity.getPrivate() == null) { + // SSH agent key + message = MessageFormat.format( + SshdText.get().authPubkeyAttemptAgent, + UserAuthPublicKey.NAME, KeyUtils.getKeyType(identity), + getKeyId(session, identity), signature); + } else { + message = MessageFormat.format( + SshdText.get().authPubkeyAttempt, + UserAuthPublicKey.NAME, KeyUtils.getKeyType(identity), + getKeyId(session, identity), signature); + } + messages.add(message); + } + + @Override + public void signalAuthenticationExhausted(ClientSession session, + String service) throws Exception { + String message; + if (hasAttempts) { + message = MessageFormat.format( + SshdText.get().authPubkeyExhausted, + UserAuthPublicKey.NAME); + } else { + message = MessageFormat.format(SshdText.get().authPubkeyNoKeys, + UserAuthPublicKey.NAME); + } + messages.add(message); + hasAttempts = false; + } + + @Override + public void signalAuthenticationFailure(ClientSession session, + String service, KeyPair identity, boolean partial, + List<String> serverMethods) throws Exception { + String message; + if (partial) { + message = MessageFormat.format( + SshdText.get().authPubkeyPartialSuccess, + UserAuthPublicKey.NAME, KeyUtils.getKeyType(identity), + getKeyId(session, identity), serverMethods); + } else { + message = MessageFormat.format( + SshdText.get().authPubkeyFailure, + UserAuthPublicKey.NAME, KeyUtils.getKeyType(identity), + getKeyId(session, identity)); + } + messages.add(message); + } + }; + + private final PasswordAuthenticationReporter passwordLogger = new PasswordAuthenticationReporter() { + + private int attempts; + + @Override + public void signalAuthenticationAttempt(ClientSession session, + String service, String oldPassword, boolean modified, + String newPassword) throws Exception { + attempts++; + String message; + if (modified) { + message = MessageFormat.format( + SshdText.get().authPasswordChangeAttempt, + UserAuthPassword.NAME, Integer.valueOf(attempts)); + } else { + message = MessageFormat.format( + SshdText.get().authPasswordAttempt, + UserAuthPassword.NAME, Integer.valueOf(attempts)); + } + messages.add(message); + } + + @Override + public void signalAuthenticationExhausted(ClientSession session, + String service) throws Exception { + String message; + if (attempts > 0) { + message = MessageFormat.format( + SshdText.get().authPasswordExhausted, + UserAuthPassword.NAME); + } else { + message = MessageFormat.format( + SshdText.get().authPasswordNotTried, + UserAuthPassword.NAME); + } + messages.add(message); + attempts = 0; + } + + @Override + public void signalAuthenticationFailure(ClientSession session, + String service, String password, boolean partial, + List<String> serverMethods) throws Exception { + String message; + if (partial) { + message = MessageFormat.format( + SshdText.get().authPasswordPartialSuccess, + UserAuthPassword.NAME, serverMethods); + } else { + message = MessageFormat.format( + SshdText.get().authPasswordFailure, + UserAuthPassword.NAME); + } + messages.add(message); + } + }; + + private final GssApiWithMicAuthenticationReporter gssLogger = new GssApiWithMicAuthenticationReporter() { + + private boolean hasAttempts; + + @Override + public void signalAuthenticationAttempt(ClientSession session, + String service, String mechanism) { + hasAttempts = true; + String message = MessageFormat.format( + SshdText.get().authGssApiAttempt, + GssApiWithMicAuthFactory.NAME, mechanism); + messages.add(message); + } + + @Override + public void signalAuthenticationExhausted(ClientSession session, + String service) { + String message; + if (hasAttempts) { + message = MessageFormat.format( + SshdText.get().authGssApiExhausted, + GssApiWithMicAuthFactory.NAME); + } else { + message = MessageFormat.format( + SshdText.get().authGssApiNotTried, + GssApiWithMicAuthFactory.NAME); + } + messages.add(message); + hasAttempts = false; + } + + @Override + public void signalAuthenticationFailure(ClientSession session, + String service, String mechanism, boolean partial, + List<String> serverMethods) { + String message; + if (partial) { + message = MessageFormat.format( + SshdText.get().authGssApiPartialSuccess, + GssApiWithMicAuthFactory.NAME, mechanism, + serverMethods); + } else { + message = MessageFormat.format( + SshdText.get().authGssApiFailure, + GssApiWithMicAuthFactory.NAME, mechanism); + } + messages.add(message); + } + }; + + /** + * Creates a new {@link AuthenticationLogger} and configures the + * {@link ClientSession} to report authentication attempts through this + * instance. + * + * @param session + * to configure + */ + public AuthenticationLogger(ClientSession session) { + session.setPublicKeyAuthenticationReporter(pubkeyLogger); + session.setPasswordAuthenticationReporter(passwordLogger); + session.setAttribute( + GssApiWithMicAuthenticationReporter.GSS_AUTHENTICATION_REPORTER, + gssLogger); + // TODO: keyboard-interactive? sshd 2.8.0 has no callback + // interface for it. + } + + /** + * Retrieves the log messages for the authentication attempts. + * + * @return the messages as an unmodifiable list + */ + public List<String> getLog() { + return Collections.unmodifiableList(messages); + } + + /** + * Drops all previously recorded log messages. + */ + public void clear() { + messages.clear(); + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java index 79b3637caa..cbd6a64140 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -11,6 +11,7 @@ package org.eclipse.jgit.internal.transport.sshd; import static java.text.MessageFormat.format; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; @@ -19,18 +20,24 @@ import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.PrivateKey; +import java.security.PublicKey; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.CancellationException; import javax.security.auth.DestroyFailedException; +import org.apache.sshd.common.AttributeRepository.AttributeKey; +import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.config.keys.FilePasswordProvider; +import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.keyprovider.FileKeyPairProvider; import org.apache.sshd.common.session.SessionContext; import org.apache.sshd.common.util.io.resource.IoResource; @@ -43,6 +50,14 @@ import org.eclipse.jgit.transport.sshd.KeyCache; public class CachingKeyPairProvider extends FileKeyPairProvider implements Iterable<KeyPair> { + /** + * An attribute set on the {@link SessionContext} recording loaded keys by + * fingerprint. This enables us to provide nicer output by showing key + * paths, if possible. Users can identify key identities used easier by + * filename than by fingerprint. + */ + public static final AttributeKey<Map<String, Path>> KEY_PATHS_BY_FINGERPRINT = new AttributeKey<>(); + private final KeyCache cache; /** @@ -78,6 +93,33 @@ public class CachingKeyPairProvider extends FileKeyPairProvider return () -> iterator(session); } + static String getKeyId(ClientSession session, KeyPair identity) { + String fingerprint = KeyUtils.getFingerPrint(identity.getPublic()); + Map<String, Path> registered = session + .getAttribute(KEY_PATHS_BY_FINGERPRINT); + if (registered != null) { + Path path = registered.get(fingerprint); + if (path != null) { + Path home = session + .resolveAttribute(JGitSshClient.HOME_DIRECTORY); + if (home != null && path.startsWith(home)) { + try { + path = home.relativize(path); + String pathString = path.toString(); + if (!pathString.isEmpty()) { + return "~" + File.separator + pathString; //$NON-NLS-1$ + } + } catch (IllegalArgumentException e) { + // Cannot be relativized. Ignore, and work with the + // original path + } + } + return path.toString(); + } + } + return fingerprint; + } + private KeyPair loadKey(SessionContext session, Path path) throws IOException, GeneralSecurityException { if (!Files.exists(path)) { @@ -123,13 +165,23 @@ public class CachingKeyPairProvider extends FileKeyPairProvider SshdText.get().identityFileUnsupportedFormat, path)); } KeyPair result = keys.next(); + PublicKey pk = result.getPublic(); + if (pk != null) { + Map<String, Path> registered = session + .getAttribute(KEY_PATHS_BY_FINGERPRINT); + if (registered == null) { + registered = new HashMap<>(); + session.setAttribute(KEY_PATHS_BY_FINGERPRINT, registered); + } + registered.put(KeyUtils.getFingerPrint(pk), path); + } if (keys.hasNext()) { log.warn(format(SshdText.get().identityFileMultipleKeys, path)); keys.forEachRemaining(k -> { - PrivateKey pk = k.getPrivate(); - if (pk != null) { + PrivateKey priv = k.getPrivate(); + if (priv != null) { try { - pk.destroy(); + priv.destroy(); } catch (DestroyFailedException e) { // Ignore } diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthentication.java index c3cac0c1df..df01db316b 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthentication.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthentication.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -18,6 +18,7 @@ import java.net.SocketAddress; import java.net.UnknownHostException; import java.util.Collection; import java.util.Iterator; +import java.util.List; import org.apache.sshd.client.auth.AbstractUserAuth; import org.apache.sshd.client.session.ClientSession; @@ -71,7 +72,10 @@ public class GssApiWithMicAuthentication extends AbstractUserAuth { if (context != null) { close(false); } + GssApiWithMicAuthenticationReporter reporter = session.getAttribute( + GssApiWithMicAuthenticationReporter.GSS_AUTHENTICATION_REPORTER); if (!nextMechanism.hasNext()) { + reporter.signalAuthenticationExhausted(session, service); return false; } state = ProtocolState.STARTED; @@ -79,6 +83,7 @@ public class GssApiWithMicAuthentication extends AbstractUserAuth { // RFC 4462 states that SPNEGO must not be used with ssh while (GssApiMechanisms.SPNEGO.equals(currentMechanism)) { if (!nextMechanism.hasNext()) { + reporter.signalAuthenticationExhausted(session, service); return false; } currentMechanism = nextMechanism.next(); @@ -102,6 +107,10 @@ public class GssApiWithMicAuthentication extends AbstractUserAuth { state = ProtocolState.FAILED; return false; } + if (reporter != null) { + reporter.signalAuthenticationAttempt(session, service, + currentMechanism.toString()); + } Buffer buffer = session .createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST); buffer.putString(session.getUsername()); @@ -246,4 +255,26 @@ public class GssApiWithMicAuthentication extends AbstractUserAuth { return false; } + @Override + public void signalAuthMethodSuccess(ClientSession session, String service, + Buffer buffer) throws Exception { + GssApiWithMicAuthenticationReporter reporter = session.getAttribute( + GssApiWithMicAuthenticationReporter.GSS_AUTHENTICATION_REPORTER); + if (reporter != null) { + reporter.signalAuthenticationSuccess(session, service, + currentMechanism.toString()); + } + } + + @Override + public void signalAuthMethodFailure(ClientSession session, String service, + boolean partial, List<String> serverMethods, Buffer buffer) + throws Exception { + GssApiWithMicAuthenticationReporter reporter = session.getAttribute( + GssApiWithMicAuthenticationReporter.GSS_AUTHENTICATION_REPORTER); + if (reporter != null) { + reporter.signalAuthenticationFailure(session, service, + currentMechanism.toString(), partial, serverMethods); + } + } } diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthenticationReporter.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthenticationReporter.java new file mode 100644 index 0000000000..201a131650 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthenticationReporter.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.internal.transport.sshd; + +import java.util.List; + +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.AttributeRepository.AttributeKey; + +/** + * Callback interface for recording authentication state in + * {@link GssApiWithMicAuthentication}. + */ +public interface GssApiWithMicAuthenticationReporter { + + /** + * An {@link AttributeKey} for a {@link ClientSession} holding the + * {@link GssApiWithMicAuthenticationReporter}. + */ + static final AttributeKey<GssApiWithMicAuthenticationReporter> GSS_AUTHENTICATION_REPORTER = new AttributeKey<>(); + + /** + * Called when a new authentication attempt is made. + * + * @param session + * the {@link ClientSession} + * @param service + * the name of the requesting SSH service name + * @param mechanism + * the OID of the mechanism used + */ + default void signalAuthenticationAttempt(ClientSession session, + String service, String mechanism) { + // nothing + } + + /** + * Called when there are no more mechanisms to try. + * + * @param session + * the {@link ClientSession} + * @param service + * the name of the requesting SSH service name + */ + default void signalAuthenticationExhausted(ClientSession session, + String service) { + // nothing + } + + /** + * Called when authentication was succeessful. + * + * @param session + * the {@link ClientSession} + * @param service + * the name of the requesting SSH service name + * @param mechanism + * the OID of the mechanism used + */ + default void signalAuthenticationSuccess(ClientSession session, + String service, String mechanism) { + // nothing + } + + /** + * Called when the authentication was not successful. + * + * @param session + * the {@link ClientSession} + * @param service + * the name of the requesting SSH service name + * @param mechanism + * the OID of the mechanism used + * @param partial + * {@code true} if authentication was partially successful, + * meaning one continues with additional authentication methods + * given by {@code serverMethods} + * @param serverMethods + * the {@link List} of authentication methods that can continue + */ + default void signalAuthenticationFailure(ClientSession session, + String service, String mechanism, boolean partial, + List<String> serverMethods) { + // nothing + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java index ff8caaacc0..33c3c608f6 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -11,13 +11,11 @@ package org.eclipse.jgit.internal.transport.sshd; import static org.apache.sshd.core.CoreModuleProperties.PASSWORD_PROMPTS; -import org.apache.sshd.client.auth.keyboard.UserInteraction; import org.apache.sshd.client.auth.password.UserAuthPassword; import org.apache.sshd.client.session.ClientSession; /** - * A password authentication handler that uses the {@link JGitUserInteraction} - * to ask the user for the password. It also respects the + * A password authentication handler that respects the * {@code NumberOfPasswordPrompts} ssh config. */ public class JGitPasswordAuthentication extends UserAuthPassword { @@ -35,30 +33,11 @@ public class JGitPasswordAuthentication extends UserAuthPassword { } @Override - protected boolean sendAuthDataRequest(ClientSession session, String service) - throws Exception { + protected String resolveAttemptedPassword(ClientSession session, + String service) throws Exception { if (++attempts > maxAttempts) { - return false; + return null; } - UserInteraction interaction = session.getUserInteraction(); - if (!interaction.isInteractionAllowed(session)) { - return false; - } - String password = getPassword(session, interaction); - if (password == null) { - throw new AuthenticationCanceledException(); - } - // sendPassword takes a buffer as first argument, but actually doesn't - // use it and creates its own buffer... - sendPassword(null, session, password, password); - return true; - } - - private String getPassword(ClientSession session, - UserInteraction interaction) { - String[] results = interaction.interactive(session, null, null, "", //$NON-NLS-1$ - new String[] { SshdText.get().passwordPrompt }, - new boolean[] { false }); - return (results == null || results.length == 0) ? null : results[0]; + return super.resolveAttemptedPassword(session, service); } } diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java index 71e8e61585..72f0bdb6ee 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -87,6 +87,11 @@ public class JGitSshClient extends SshClient { public static final AttributeKey<String> PREFERRED_AUTHENTICATIONS = new AttributeKey<>(); /** + * An attribute key for the home directory. + */ + public static final AttributeKey<Path> HOME_DIRECTORY = new AttributeKey<>(); + + /** * An attribute key for storing an alternate local address to connect to if * a local forward from a ProxyJump ssh config is present. If set, * {@link #connect(HostConfigEntry, AttributeRepository, SocketAddress)} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitUserInteraction.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitUserInteraction.java index c51a75bc6f..2a725ea16a 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitUserInteraction.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitUserInteraction.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -120,15 +120,16 @@ public class JGitUserInteraction implements UserInteraction { return null; }).filter(s -> s != null).toArray(String[]::new); } - // TODO What to throw to abort the connection/authentication process? - // In UserAuthKeyboardInteractive.getUserResponses() it's clear that - // returning null is valid and signifies "an error"; we'll try the - // next authentication method. But if the user explicitly canceled, - // then we don't want to try the next methods... - // - // Probably not a serious issue with the typical order of public-key, - // keyboard-interactive, password. - return null; + throw new AuthenticationCanceledException(); + } + + @Override + public String resolveAuthPasswordAttempt(ClientSession session) + throws Exception { + String[] results = interactive(session, null, null, "", //$NON-NLS-1$ + new String[] { SshdText.get().passwordPrompt }, + new boolean[] { false }); + return (results == null || results.length == 0) ? null : results[0]; } @Override 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 19ad85c83d..39332d9fca 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, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -29,6 +29,23 @@ public final class SshdText extends TranslationBundle { // @formatter:off /***/ public String authenticationCanceled; /***/ public String authenticationOnClosedSession; + /***/ public String authGssApiAttempt; + /***/ public String authGssApiExhausted; + /***/ public String authGssApiFailure; + /***/ public String authGssApiNotTried; + /***/ public String authGssApiPartialSuccess; + /***/ public String authPasswordAttempt; + /***/ public String authPasswordChangeAttempt; + /***/ public String authPasswordExhausted; + /***/ public String authPasswordFailure; + /***/ public String authPasswordNotTried; + /***/ public String authPasswordPartialSuccess; + /***/ public String authPubkeyAttempt; + /***/ public String authPubkeyAttemptAgent; + /***/ public String authPubkeyExhausted; + /***/ public String authPubkeyFailure; + /***/ public String authPubkeyNoKeys; + /***/ public String authPubkeyPartialSuccess; /***/ public String closeListenerFailed; /***/ public String cannotReadPublicKey; /***/ public String configInvalidPath; diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java index b742f5ea42..b94ccc6d4f 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -52,6 +52,8 @@ import org.apache.sshd.sftp.common.SftpException; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile; +import org.eclipse.jgit.internal.transport.sshd.AuthenticationCanceledException; +import org.eclipse.jgit.internal.transport.sshd.AuthenticationLogger; import org.eclipse.jgit.internal.transport.sshd.JGitSshClient; import org.eclipse.jgit.internal.transport.sshd.SshdText; import org.eclipse.jgit.transport.FtpChannel; @@ -119,6 +121,7 @@ public class SshdSession implements RemoteSession2 { ClientSession resultSession = null; ClientSession proxySession = null; PortForwardingTracker portForward = null; + AuthenticationLogger authLog = null; try { if (!hops.isEmpty()) { URIish hop = hops.remove(0); @@ -165,6 +168,7 @@ public class SshdSession implements RemoteSession2 { resultSession.addCloseFutureListener(listener); } // Authentication timeout is by default 2 minutes. + authLog = new AuthenticationLogger(resultSession); resultSession.auth().verify(resultSession.getAuthTimeout()); return resultSession; } catch (IOException e) { @@ -173,15 +177,32 @@ public class SshdSession implements RemoteSession2 { close(resultSession, e); if (e instanceof SshException && ((SshException) e) .getDisconnectCode() == SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE) { - // Ensure the user gets to know on which URI the authentication - // was denied. + String message = format(SshdText.get().loginDenied, host, + Integer.toString(port)); throw new TransportException(target, - format(SshdText.get().loginDenied, host, - Integer.toString(port)), - e); + withAuthLog(message, authLog), e); + } else if (e instanceof SshException && e + .getCause() instanceof AuthenticationCanceledException) { + String message = e.getCause().getMessage(); + throw new TransportException(target, + withAuthLog(message, authLog), e.getCause()); } throw e; + } finally { + if (authLog != null) { + authLog.clear(); + } + } + } + + private String withAuthLog(String message, AuthenticationLogger authLog) { + if (authLog != null) { + String log = String.join(System.lineSeparator(), authLog.getLog()); + if (!log.isEmpty()) { + return message + System.lineSeparator() + log; + } } + return message; } private ClientSession connect(HostConfigEntry config, 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 58cf8e1ddd..c792c1889c 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, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -13,6 +13,7 @@ import java.io.Closeable; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.security.KeyPair; import java.time.Duration; @@ -34,7 +35,6 @@ import org.apache.sshd.client.auth.UserAuthFactory; import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory; import org.apache.sshd.client.config.hosts.HostConfigEntryResolver; import org.apache.sshd.common.NamedFactory; -import org.apache.sshd.common.SshException; import org.apache.sshd.common.compression.BuiltinCompressions; import org.apache.sshd.common.config.keys.FilePasswordProvider; import org.apache.sshd.common.config.keys.loader.openssh.kdf.BCryptKdfOptions; @@ -44,7 +44,6 @@ import org.apache.sshd.common.signature.Signature; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile; -import org.eclipse.jgit.internal.transport.sshd.AuthenticationCanceledException; import org.eclipse.jgit.internal.transport.sshd.CachingKeyPairProvider; import org.eclipse.jgit.internal.transport.sshd.GssApiWithMicAuthFactory; import org.eclipse.jgit.internal.transport.sshd.JGitPasswordAuthFactory; @@ -243,6 +242,12 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable { JGitSshClient.PREFERRED_AUTHENTICATIONS, defaultAuths); } + try { + jgitClient.setAttribute(JGitSshClient.HOME_DIRECTORY, + home.getAbsoluteFile().toPath()); + } catch (SecurityException | InvalidPathException e) { + // Ignore + } // Other things? return client; }); @@ -255,13 +260,7 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable { if (e instanceof TransportException) { throw (TransportException) e; } - Throwable cause = e; - if (e instanceof SshException && e - .getCause() instanceof AuthenticationCanceledException) { - // Results in a nicer error message - cause = e.getCause(); - } - throw new TransportException(uri, cause.getMessage(), cause); + throw new TransportException(uri, e.getMessage(), e); } } 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 4a08940c83..f48b926402 100644 --- a/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF @@ -3,18 +3,18 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.ssh.jsch.test Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch.test -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-11 Import-Package: com.jcraft.jsch;version="[0.1.54,0.2.0)", - org.eclipse.jgit.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit.ssh;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.ssh.jsch;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", + org.eclipse.jgit.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit.ssh;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.ssh.jsch;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.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 ca1e742344..9a3a847951 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-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 332d981cf9..4238239b5b 100644 --- a/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF @@ -3,19 +3,19 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.ssh.jsch Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch;singleton:=true -Fragment-Host: org.eclipse.jgit;bundle-version="[6.1.1,6.2.0)" +Fragment-Host: org.eclipse.jgit;bundle-version="[6.2.0,6.3.0)" Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-ActivationPolicy: lazy -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 -Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="6.1.1" +Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="6.2.0" Import-Package: com.jcraft.jsch;version="[0.1.37,0.2.0)", - org.eclipse.jgit.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.transport.ssh;version="[6.1.1,6.2.0)", - org.eclipse.jgit.nls;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util.io;version="[6.1.1,6.2.0)", + org.eclipse.jgit.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.transport.ssh;version="[6.2.0,6.3.0)", + org.eclipse.jgit.nls;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util.io;version="[6.2.0,6.3.0)", org.slf4j;version="[1.7.0,2.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 b56bb7c6e0..edb6b195fd 100644 --- a/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.ssh.jsch - Sources Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.ssh.jsch/pom.xml b/org.eclipse.jgit.ssh.jsch/pom.xml index 3b6d66c68c..1ff971f835 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-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 581395d90c..85d93a95d6 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.test Bundle-SymbolicName: org.eclipse.jgit.test -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor Bundle-RequiredExecutionEnvironment: JavaSE-11 @@ -16,61 +16,61 @@ Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)", org.apache.commons.compress.compressors.gzip;version="[1.15.0,2.0)", org.apache.commons.compress.compressors.xz;version="[1.15.0,2.0)", org.assertj.core.api;version="[3.14.0,4.0.0)", - org.eclipse.jgit.annotations;version="[6.1.1,6.2.0)", - org.eclipse.jgit.api;version="[6.1.1,6.2.0)", - org.eclipse.jgit.api.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.archive;version="[6.1.1,6.2.0)", - org.eclipse.jgit.attributes;version="[6.1.1,6.2.0)", - org.eclipse.jgit.awtui;version="[6.1.1,6.2.0)", - org.eclipse.jgit.blame;version="[6.1.1,6.2.0)", - org.eclipse.jgit.diff;version="[6.1.1,6.2.0)", - org.eclipse.jgit.dircache;version="[6.1.1,6.2.0)", - org.eclipse.jgit.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.events;version="[6.1.1,6.2.0)", - org.eclipse.jgit.fnmatch;version="[6.1.1,6.2.0)", - org.eclipse.jgit.gitrepo;version="[6.1.1,6.2.0)", - org.eclipse.jgit.hooks;version="[6.1.1,6.2.0)", - org.eclipse.jgit.ignore;version="[6.1.1,6.2.0)", - org.eclipse.jgit.ignore.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.diffmergetool;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.fsck;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.dfs;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.io;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.pack;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.storage.reftable;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.transport.connectivity;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.transport.http;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.transport.parser;version="[6.1.1,6.2.0)", - org.eclipse.jgit.internal.transport.ssh;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit;version="[6.1.1,6.2.0)", - org.eclipse.jgit.junit.time;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lfs;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.logging;version="[6.1.1,6.2.0)", - org.eclipse.jgit.merge;version="[6.1.1,6.2.0)", - org.eclipse.jgit.nls;version="[6.1.1,6.2.0)", - org.eclipse.jgit.notes;version="[6.1.1,6.2.0)", - org.eclipse.jgit.patch;version="[6.1.1,6.2.0)", - org.eclipse.jgit.pgm;version="[6.1.1,6.2.0)", - org.eclipse.jgit.pgm.internal;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revplot;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk.filter;version="[6.1.1,6.2.0)", - org.eclipse.jgit.storage.file;version="[6.1.1,6.2.0)", - org.eclipse.jgit.storage.pack;version="[6.1.1,6.2.0)", - org.eclipse.jgit.submodule;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.http;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport.resolver;version="[6.1.1,6.2.0)", - org.eclipse.jgit.treewalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.treewalk.filter;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util.io;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util.sha1;version="[6.1.1,6.2.0)", + org.eclipse.jgit.annotations;version="[6.2.0,6.3.0)", + org.eclipse.jgit.api;version="[6.2.0,6.3.0)", + org.eclipse.jgit.api.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.archive;version="[6.2.0,6.3.0)", + org.eclipse.jgit.attributes;version="[6.2.0,6.3.0)", + org.eclipse.jgit.awtui;version="[6.2.0,6.3.0)", + org.eclipse.jgit.blame;version="[6.2.0,6.3.0)", + org.eclipse.jgit.diff;version="[6.2.0,6.3.0)", + org.eclipse.jgit.dircache;version="[6.2.0,6.3.0)", + org.eclipse.jgit.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.events;version="[6.2.0,6.3.0)", + org.eclipse.jgit.fnmatch;version="[6.2.0,6.3.0)", + org.eclipse.jgit.gitrepo;version="[6.2.0,6.3.0)", + org.eclipse.jgit.hooks;version="[6.2.0,6.3.0)", + org.eclipse.jgit.ignore;version="[6.2.0,6.3.0)", + org.eclipse.jgit.ignore.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.diffmergetool;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.fsck;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.dfs;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.io;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.pack;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.storage.reftable;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.transport.connectivity;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.transport.http;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.transport.parser;version="[6.2.0,6.3.0)", + org.eclipse.jgit.internal.transport.ssh;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit;version="[6.2.0,6.3.0)", + org.eclipse.jgit.junit.time;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lfs;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.logging;version="[6.2.0,6.3.0)", + org.eclipse.jgit.merge;version="[6.2.0,6.3.0)", + org.eclipse.jgit.nls;version="[6.2.0,6.3.0)", + org.eclipse.jgit.notes;version="[6.2.0,6.3.0)", + org.eclipse.jgit.patch;version="[6.2.0,6.3.0)", + org.eclipse.jgit.pgm;version="[6.2.0,6.3.0)", + org.eclipse.jgit.pgm.internal;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revplot;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk.filter;version="[6.2.0,6.3.0)", + org.eclipse.jgit.storage.file;version="[6.2.0,6.3.0)", + org.eclipse.jgit.storage.pack;version="[6.2.0,6.3.0)", + org.eclipse.jgit.submodule;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.http;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport.resolver;version="[6.2.0,6.3.0)", + org.eclipse.jgit.treewalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.treewalk.filter;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util.io;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util.sha1;version="[6.2.0,6.3.0)", org.hamcrest;version="[1.1.0,3.0.0)", org.hamcrest.collection;version="[1.1.0,3.0.0)", org.junit;version="[4.13,5.0.0)", diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml index 6b66919894..0d1e5458fc 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.test</artifactId> diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java index 6479d157eb..0884d72235 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java @@ -96,6 +96,53 @@ public class FetchCommandTest extends RepositoryTestCase { } @Test + public void testFetchSimpleNegativeRefSpec() throws Exception { + remoteGit.commit().setMessage("commit").call(); + + FetchResult res = git.fetch().setRemote("test") + .setRefSpecs("refs/heads/master:refs/heads/test", + "^:refs/heads/test") + .call(); + assertNull(res.getTrackingRefUpdate("refs/heads/test")); + + res = git.fetch().setRemote("test") + .setRefSpecs("refs/heads/master:refs/heads/test", + "^refs/heads/master") + .call(); + assertNull(res.getTrackingRefUpdate("refs/heads/test")); + } + + @Test + public void negativeRefSpecFilterBySource() throws Exception { + remoteGit.commit().setMessage("commit").call(); + remoteGit.branchCreate().setName("test").call(); + remoteGit.commit().setMessage("commit1").call(); + remoteGit.branchCreate().setName("dev").call(); + + FetchResult res = git.fetch().setRemote("test") + .setRefSpecs("refs/*:refs/origins/*", "^refs/*/test") + .call(); + assertNotNull(res.getTrackingRefUpdate("refs/origins/heads/master")); + assertNull(res.getTrackingRefUpdate("refs/origins/heads/test")); + assertNotNull(res.getTrackingRefUpdate("refs/origins/heads/dev")); + } + + @Test + public void negativeRefSpecFilterByDestination() throws Exception { + remoteGit.commit().setMessage("commit").call(); + remoteGit.branchCreate().setName("meta").call(); + remoteGit.commit().setMessage("commit1").call(); + remoteGit.branchCreate().setName("data").call(); + + FetchResult res = git.fetch().setRemote("test") + .setRefSpecs("refs/*:refs/secret/*", "^:refs/secret/*/meta") + .call(); + assertNotNull(res.getTrackingRefUpdate("refs/secret/heads/master")); + assertNull(res.getTrackingRefUpdate("refs/secret/heads/meta")); + assertNotNull(res.getTrackingRefUpdate("refs/secret/heads/data")); + } + + @Test public void fetchAddsBranches() throws Exception { final String branch1 = "b1"; final String branch2 = "b2"; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java index 12ec2aae57..05af175cfa 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java @@ -21,6 +21,8 @@ import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.util.SystemReader; import org.junit.Test; public class LsRemoteCommandTest extends RepositoryTestCase { @@ -107,6 +109,20 @@ public class LsRemoteCommandTest extends RepositoryTestCase { } @Test + public void testLsRemoteWithoutLocalRepositoryUrlInsteadOf() + throws Exception { + String uri = fileUri(); + StoredConfig userConfig = SystemReader.getInstance().getUserConfig(); + userConfig.load(); + userConfig.setString("url", uri, "insteadOf", "file:///foo"); + userConfig.save(); + Collection<Ref> refs = Git.lsRemoteRepository().setRemote("file:///foo") + .setHeads(true).call(); + assertNotNull(refs); + assertEquals(2, refs.size()); + } + + @Test public void testLsRemoteWithSymRefs() throws Exception { File directory = createTempDirectory("testRepository"); CloneCommand command = Git.cloneRepository(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java index 64475f5d50..917b6c3297 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java @@ -36,6 +36,7 @@ import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryState; import org.eclipse.jgit.lib.Sets; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.merge.ContentMergeStrategy; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; @@ -2018,6 +2019,73 @@ public class MergeCommandTest extends RepositoryTestCase { } } + @Test + public void testMergeConflictWithMessageAndCommentChar() throws Exception { + try (Git git = new Git(db)) { + writeTrashFile("a", "1\na\n3\n"); + git.add().addFilepattern("a").call(); + RevCommit initialCommit = git.commit().setMessage("initial").call(); + + createBranch(initialCommit, "refs/heads/side"); + checkoutBranch("refs/heads/side"); + + writeTrashFile("a", "1\na(side)\n3\n"); + git.add().addFilepattern("a").call(); + git.commit().setMessage("side").call(); + + checkoutBranch("refs/heads/master"); + + writeTrashFile("a", "1\na(main)\n3\n"); + git.add().addFilepattern("a").call(); + git.commit().setMessage("main").call(); + + StoredConfig config = db.getConfig(); + config.setString("core", null, "commentChar", "^"); + + Ref sideBranch = db.exactRef("refs/heads/side"); + + git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE) + .setMessage("user message").call(); + + assertEquals("user message\n\n^ Conflicts:\n^\ta\n", + db.readMergeCommitMsg()); + } + } + + @Test + public void testMergeConflictWithMessageAndCommentCharAuto() + throws Exception { + try (Git git = new Git(db)) { + writeTrashFile("a", "1\na\n3\n"); + git.add().addFilepattern("a").call(); + RevCommit initialCommit = git.commit().setMessage("initial").call(); + + createBranch(initialCommit, "refs/heads/side"); + checkoutBranch("refs/heads/side"); + + writeTrashFile("a", "1\na(side)\n3\n"); + git.add().addFilepattern("a").call(); + git.commit().setMessage("side").call(); + + checkoutBranch("refs/heads/master"); + + writeTrashFile("a", "1\na(main)\n3\n"); + git.add().addFilepattern("a").call(); + git.commit().setMessage("main").call(); + + StoredConfig config = db.getConfig(); + config.setString("core", null, "commentChar", "auto"); + + Ref sideBranch = db.exactRef("refs/heads/side"); + + git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE) + .setMessage("#user message").call(); + + assertEquals("#user message\n\n; Conflicts:\n;\ta\n", + db.readMergeCommitMsg()); + } + } + private static void setExecutable(Git git, String path, boolean executable) { FS.DETECTED.setExecute( new File(git.getRepository().getWorkTree(), path), executable); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java index c64ff0b1c3..d574e45f6f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java @@ -30,6 +30,7 @@ import java.util.List; import org.eclipse.jgit.api.MergeResult.MergeStatus; import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler; +import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler2; import org.eclipse.jgit.api.RebaseCommand.Operation; import org.eclipse.jgit.api.RebaseResult.Status; import org.eclipse.jgit.api.errors.InvalidRebaseStepException; @@ -46,6 +47,7 @@ import org.eclipse.jgit.events.ChangeRecorder; import org.eclipse.jgit.events.ListenerHandle; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.AbbreviatedObjectId; +import org.eclipse.jgit.lib.CommitConfig.CleanupMode; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; @@ -56,6 +58,7 @@ import org.eclipse.jgit.lib.RebaseTodoLine.Action; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.ReflogEntry; import org.eclipse.jgit.lib.RepositoryState; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevSort; @@ -3410,6 +3413,99 @@ public class RebaseCommandTest extends RepositoryTestCase { } + @Test + public void testInteractiveRebaseSquashFixupSequence() throws Exception { + // create file1, add and commit + writeTrashFile(FILE1, "file1"); + git.add().addFilepattern(FILE1).call(); + git.commit().setMessage("commit1").call(); + + // modify file1, add and commit + writeTrashFile(FILE1, "modified file1"); + git.add().addFilepattern(FILE1).call(); + git.commit().setMessage("commit2").call(); + + // modify file1, add and commit + writeTrashFile(FILE1, "modified file1 a second time"); + git.add().addFilepattern(FILE1).call(); + // Make it difficult; use git standard comment characters in the commit + // messages + git.commit().setMessage("#commit3").call(); + + // modify file1, add and commit + writeTrashFile(FILE1, "modified file1 a third time"); + git.add().addFilepattern(FILE1).call(); + git.commit().setMessage("@commit4").call(); + + // modify file1, add and commit + writeTrashFile(FILE1, "modified file1 a fourth time"); + git.add().addFilepattern(FILE1).call(); + git.commit().setMessage(";commit5").call(); + + StoredConfig config = git.getRepository().getConfig(); + config.setString("core", null, "commentChar", "auto"); + // With "auto", we should end up with '@' being used as comment + // character (commit4 is skipped, so it should not advance the + // character). + RebaseResult result = git.rebase().setUpstream("HEAD~4") + .runInteractively(new InteractiveHandler2() { + + @Override + public void prepareSteps(List<RebaseTodoLine> steps) { + try { + steps.get(0).setAction(Action.PICK); + steps.get(1).setAction(Action.SQUASH); + steps.get(2).setAction(Action.FIXUP); + steps.get(3).setAction(Action.SQUASH); + } catch (IllegalTodoFileModification e) { + fail("unexpected exception: " + e); + } + } + + @Override + public String modifyCommitMessage(String commit) { + fail("should not be called"); + return commit; + } + + @Override + public ModifyResult editCommitMessage(String message, + CleanupMode mode, char commentChar) { + assertEquals('@', commentChar); + assertEquals("@ This is a combination of 4 commits.\n" + + "@ The first commit's message is:\n" + + "commit2\n" + + "@ This is the 2nd commit message:\n" + + "#commit3\n" + + "@ The 3rd commit message will be skipped:\n" + + "@ @commit4\n" + + "@ This is the 4th commit message:\n" + + ";commit5", message); + return new ModifyResult() { + + @Override + public String getMessage() { + return message; + } + + @Override + public CleanupMode getCleanupMode() { + return mode; + } + + @Override + public boolean shouldAddChangeId() { + return false; + } + }; + } + }).call(); + assertEquals(Status.OK, result.getStatus()); + Iterator<RevCommit> logIterator = git.log().all().call().iterator(); + String actualCommitMsg = logIterator.next().getFullMessage(); + assertEquals("commit2\n#commit3\n;commit5", actualCommitMsg); + } + private File getTodoFile() { File todoFile = new File(db.getDirectory(), GIT_REBASE_TODO); return todoFile; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalDiffToolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalDiffToolTest.java index c9ebec7638..f69a1794ef 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalDiffToolTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalDiffToolTest.java @@ -10,21 +10,36 @@ package org.eclipse.jgit.internal.diffmergetool; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFFTOOL_SECTION; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CMD; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_GUITOOL; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PATH; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TRUST_EXIT_CODE; 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 static org.junit.Assert.fail; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.internal.BooleanTriState; import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.util.FS.ExecutionResult; import org.junit.Test; /** @@ -32,20 +47,119 @@ import org.junit.Test; */ public class ExternalDiffToolTest extends ExternalToolTestCase { + @Test(expected = ToolException.class) + public void testUserToolWithError() throws Exception { + String toolName = "customTool"; + + int errorReturnCode = 1; + String command = "exit " + errorReturnCode; + + FileBasedConfig config = db.getConfig(); + config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD, + command); + + invokeCompare(toolName); + + fail("Expected exception to be thrown due to external tool exiting with error code: " + + errorReturnCode); + } + + @Test(expected = ToolException.class) + public void testUserToolWithCommandNotFoundError() throws Exception { + String toolName = "customTool"; + + int errorReturnCode = 127; // command not found + String command = "exit " + errorReturnCode; + + FileBasedConfig config = db.getConfig(); + config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD, + command); + + invokeCompare(toolName); + fail("Expected exception to be thrown due to external tool exiting with error code: " + + errorReturnCode); + } + + @Test + public void testUserDefinedTool() throws Exception { + String command = getEchoCommand(); + + FileBasedConfig config = db.getConfig(); + String customToolName = "customTool"; + config.setString(CONFIG_DIFFTOOL_SECTION, customToolName, + CONFIG_KEY_CMD, command); + + DiffTools manager = new DiffTools(db); + + Map<String, ExternalDiffTool> tools = manager.getUserDefinedTools(); + ExternalDiffTool externalTool = tools.get(customToolName); + boolean trustExitCode = true; + manager.compare(local, remote, externalTool, trustExitCode); + + assertEchoCommandHasCorrectOutput(); + } + + @Test + public void testUserDefinedToolWithPrompt() throws Exception { + String command = getEchoCommand(); + + FileBasedConfig config = db.getConfig(); + String customToolName = "customTool"; + config.setString(CONFIG_DIFFTOOL_SECTION, customToolName, + CONFIG_KEY_CMD, command); + + DiffTools manager = new DiffTools(db); + + PromptHandler promptHandler = PromptHandler.acceptPrompt(); + MissingToolHandler noToolHandler = new MissingToolHandler(); + + manager.compare(local, remote, Optional.of(customToolName), + BooleanTriState.TRUE, false, BooleanTriState.TRUE, + promptHandler, noToolHandler); + + assertEchoCommandHasCorrectOutput(); + + List<String> actualToolPrompts = promptHandler.toolPrompts; + List<String> expectedToolPrompts = Arrays.asList("customTool"); + assertEquals("Expected a user prompt for custom tool call", + expectedToolPrompts, actualToolPrompts); + + assertEquals("Expected to no informing about missing tools", + Collections.EMPTY_LIST, noToolHandler.missingTools); + } + @Test - public void testToolNames() { + public void testUserDefinedToolWithCancelledPrompt() throws Exception { + String command = getEchoCommand(); + + FileBasedConfig config = db.getConfig(); + String customToolName = "customTool"; + config.setString(CONFIG_DIFFTOOL_SECTION, customToolName, + CONFIG_KEY_CMD, command); + DiffTools manager = new DiffTools(db); - Set<String> actualToolNames = manager.getToolNames(); - Set<String> expectedToolNames = Collections.emptySet(); - assertEquals("Incorrect set of external diff tool names", - expectedToolNames, actualToolNames); + + PromptHandler promptHandler = PromptHandler.cancelPrompt(); + MissingToolHandler noToolHandler = new MissingToolHandler(); + + Optional<ExecutionResult> result = manager.compare(local, remote, + Optional.of(customToolName), BooleanTriState.TRUE, false, + BooleanTriState.TRUE, promptHandler, noToolHandler); + assertFalse("Expected no result if user cancels the operation", + result.isPresent()); } @Test public void testAllTools() { + FileBasedConfig config = db.getConfig(); + String customToolName = "customTool"; + config.setString(CONFIG_DIFFTOOL_SECTION, customToolName, + CONFIG_KEY_CMD, "echo"); + DiffTools manager = new DiffTools(db); - Set<String> actualToolNames = manager.getAvailableTools().keySet(); + Set<String> actualToolNames = manager.getAllToolNames(); Set<String> expectedToolNames = new LinkedHashSet<>(); + expectedToolNames.add(customToolName); CommandLineDiffTool[] defaultTools = CommandLineDiffTool.values(); for (CommandLineDiffTool defaultTool : defaultTools) { String toolName = defaultTool.name(); @@ -86,11 +200,11 @@ public class ExternalDiffToolTest extends ExternalToolTestCase { config.setString(CONFIG_DIFFTOOL_SECTION, customToolname, CONFIG_KEY_PATH, "/usr/bin/echo"); config.setString(CONFIG_DIFFTOOL_SECTION, customToolname, - CONFIG_KEY_PROMPT, "--no-prompt"); + CONFIG_KEY_PROMPT, String.valueOf(false)); config.setString(CONFIG_DIFFTOOL_SECTION, customToolname, - CONFIG_KEY_GUITOOL, "--no-gui"); + CONFIG_KEY_GUITOOL, String.valueOf(false)); config.setString(CONFIG_DIFFTOOL_SECTION, customToolname, - CONFIG_KEY_TRUST_EXIT_CODE, "--no-trust-exit-code"); + CONFIG_KEY_TRUST_EXIT_CODE, String.valueOf(false)); DiffTools manager = new DiffTools(db); Set<String> actualToolNames = manager.getUserDefinedTools().keySet(); Set<String> expectedToolNames = new LinkedHashSet<>(); @@ -100,59 +214,240 @@ public class ExternalDiffToolTest extends ExternalToolTestCase { } @Test - public void testNotAvailableTools() { - DiffTools manager = new DiffTools(db); - Set<String> actualToolNames = manager.getNotAvailableTools().keySet(); - Set<String> expectedToolNames = Collections.emptySet(); - assertEquals("Incorrect set of not available external diff tools", - expectedToolNames, actualToolNames); - } + public void testCompare() throws ToolException { + String toolName = "customTool"; - @Test - public void testCompare() { - DiffTools manager = new DiffTools(db); + FileBasedConfig config = db.getConfig(); + // the default diff tool is configured without a subsection + String subsection = null; + config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_TOOL, + toolName); - String newPath = ""; - String oldPath = ""; - String newId = ""; - String oldId = ""; - String toolName = ""; - BooleanTriState prompt = BooleanTriState.UNSET; - BooleanTriState gui = BooleanTriState.UNSET; - BooleanTriState trustExitCode = BooleanTriState.UNSET; + String command = getEchoCommand(); + config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD, + command); + Optional<ExecutionResult> result = invokeCompare(toolName); + assertTrue("Expected external diff tool result to be available", + result.isPresent()); int expectedCompareResult = 0; - int compareResult = manager.compare(newPath, oldPath, newId, oldId, - toolName, prompt, gui, trustExitCode); assertEquals("Incorrect compare result for external diff tool", - expectedCompareResult, compareResult); + expectedCompareResult, result.get().getRc()); } @Test public void testDefaultTool() throws Exception { + String toolName = "customTool"; + String guiToolName = "customGuiTool"; + FileBasedConfig config = db.getConfig(); // the default diff tool is configured without a subsection String subsection = null; - config.setString("diff", subsection, "tool", "customTool"); + config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_TOOL, + toolName); DiffTools manager = new DiffTools(db); - BooleanTriState gui = BooleanTriState.UNSET; + boolean gui = false; String defaultToolName = manager.getDefaultToolName(gui); assertEquals( "Expected configured difftool to be the default external diff tool", - "my_default_toolname", defaultToolName); + toolName, defaultToolName); - gui = BooleanTriState.TRUE; + gui = true; String defaultGuiToolName = manager.getDefaultToolName(gui); assertEquals( - "Expected configured difftool to be the default external diff tool", - "my_gui_tool", defaultGuiToolName); + "Expected default gui difftool to be the default tool if no gui tool is set", + toolName, defaultGuiToolName); - config.setString("diff", subsection, "guitool", "customGuiTool"); + config.setString(CONFIG_DIFF_SECTION, subsection, CONFIG_KEY_GUITOOL, + guiToolName); manager = new DiffTools(db); defaultGuiToolName = manager.getDefaultToolName(gui); assertEquals( "Expected configured difftool to be the default external diff guitool", - "my_gui_tool", defaultGuiToolName); + guiToolName, defaultGuiToolName); + } + + @Test + public void testOverridePreDefinedToolPath() { + String newToolPath = "/tmp/path/"; + + CommandLineDiffTool[] defaultTools = CommandLineDiffTool.values(); + assertTrue("Expected to find pre-defined external diff tools", + defaultTools.length > 0); + + CommandLineDiffTool overridenTool = defaultTools[0]; + String overridenToolName = overridenTool.name(); + String overridenToolPath = newToolPath + overridenToolName; + FileBasedConfig config = db.getConfig(); + config.setString(CONFIG_DIFFTOOL_SECTION, overridenToolName, + CONFIG_KEY_PATH, overridenToolPath); + + DiffTools manager = new DiffTools(db); + Map<String, ExternalDiffTool> availableTools = manager + .getPredefinedTools(true); + ExternalDiffTool externalDiffTool = availableTools + .get(overridenToolName); + String actualDiffToolPath = externalDiffTool.getPath(); + assertEquals( + "Expected pre-defined external diff tool to have overriden path", + overridenToolPath, actualDiffToolPath); + String expectedDiffToolCommand = overridenToolPath + " " + + overridenTool.getParameters(); + String actualDiffToolCommand = externalDiffTool.getCommand(); + assertEquals( + "Expected pre-defined external diff tool to have overriden command", + expectedDiffToolCommand, actualDiffToolCommand); + } + + @Test(expected = ToolException.class) + public void testUndefinedTool() throws Exception { + String toolName = "undefined"; + invokeCompare(toolName); + fail("Expected exception to be thrown due to not defined external diff tool"); + } + + @Test + public void testDefaultToolExecutionWithPrompt() throws Exception { + FileBasedConfig config = db.getConfig(); + // the default diff tool is configured without a subsection + String subsection = null; + config.setString("diff", subsection, "tool", "customTool"); + + String command = getEchoCommand(); + + config.setString("difftool", "customTool", "cmd", command); + + DiffTools manager = new DiffTools(db); + + PromptHandler promptHandler = PromptHandler.acceptPrompt(); + MissingToolHandler noToolHandler = new MissingToolHandler(); + + manager.compare(local, remote, Optional.empty(), BooleanTriState.TRUE, + false, BooleanTriState.TRUE, promptHandler, noToolHandler); + + assertEchoCommandHasCorrectOutput(); + } + + @Test + public void testNoDefaultToolName() { + DiffTools manager = new DiffTools(db); + boolean gui = false; + String defaultToolName = manager.getDefaultToolName(gui); + assertNull("Expected no default tool when none is configured", + defaultToolName); + + gui = true; + defaultToolName = manager.getDefaultToolName(gui); + assertNull("Expected no default tool when none is configured", + defaultToolName); + } + + @Test + public void testExternalToolInGitAttributes() throws Exception { + String content = "attributes:\n*.txt difftool=customTool"; + File gitattributes = writeTrashFile(".gitattributes", content); + gitattributes.deleteOnExit(); + try (TestRepository<Repository> testRepository = new TestRepository<>( + db)) { + FileBasedConfig config = db.getConfig(); + config.setString("difftool", "customTool", "cmd", "echo"); + testRepository.git().add().addFilepattern(localFile.getName()) + .call(); + + testRepository.git().add().addFilepattern(".gitattributes").call(); + + testRepository.branch("master").commit().message("first commit") + .create(); + + DiffTools manager = new DiffTools(db); + Optional<String> tool = manager + .getExternalToolFromAttributes(localFile.getName()); + assertTrue("Failed to find user defined tool", tool.isPresent()); + assertEquals("Failed to find user defined tool", "customTool", + tool.get()); + } finally { + Files.delete(gitattributes.toPath()); + } + } + + @Test + public void testNotExternalToolInGitAttributes() throws Exception { + String content = ""; + File gitattributes = writeTrashFile(".gitattributes", content); + gitattributes.deleteOnExit(); + try (TestRepository<Repository> testRepository = new TestRepository<>( + db)) { + FileBasedConfig config = db.getConfig(); + config.setString("difftool", "customTool", "cmd", "echo"); + testRepository.git().add().addFilepattern(localFile.getName()) + .call(); + + testRepository.git().add().addFilepattern(".gitattributes").call(); + + testRepository.branch("master").commit().message("first commit") + .create(); + + DiffTools manager = new DiffTools(db); + Optional<String> tool = manager + .getExternalToolFromAttributes(localFile.getName()); + assertFalse( + "Expected no external tool if no default tool is specified in .gitattributes", + tool.isPresent()); + } finally { + Files.delete(gitattributes.toPath()); + } + } + + @Test(expected = ToolException.class) + public void testNullTool() throws Exception { + DiffTools manager = new DiffTools(db); + + boolean trustExitCode = true; + ExternalDiffTool tool = null; + manager.compare(local, remote, tool, trustExitCode); + } + + @Test(expected = ToolException.class) + public void testNullToolWithPrompt() throws Exception { + DiffTools manager = new DiffTools(db); + + PromptHandler promptHandler = PromptHandler.cancelPrompt(); + MissingToolHandler noToolHandler = new MissingToolHandler(); + + Optional<String> tool = null; + manager.compare(local, remote, tool, BooleanTriState.TRUE, false, + BooleanTriState.TRUE, promptHandler, noToolHandler); + } + + private Optional<ExecutionResult> invokeCompare(String toolName) + throws ToolException { + DiffTools manager = new DiffTools(db); + + BooleanTriState prompt = BooleanTriState.UNSET; + boolean gui = false; + BooleanTriState trustExitCode = BooleanTriState.TRUE; + PromptHandler promptHandler = PromptHandler.acceptPrompt(); + MissingToolHandler noToolHandler = new MissingToolHandler(); + + Optional<ExecutionResult> result = manager.compare(local, remote, + Optional.of(toolName), prompt, gui, trustExitCode, + promptHandler, noToolHandler); + return result; + } + + private String getEchoCommand() { + return "(echo \"$LOCAL\" \"$REMOTE\") > " + + commandResult.getAbsolutePath(); + } + + private void assertEchoCommandHasCorrectOutput() throws IOException { + List<String> actualLines = Files.readAllLines(commandResult.toPath()); + String actualContent = String.join(System.lineSeparator(), actualLines); + actualLines = Arrays.asList(actualContent.split(" ")); + List<String> expectedLines = Arrays.asList(localFile.getAbsolutePath(), + remoteFile.getAbsolutePath()); + assertEquals("Dummy test tool called with unexpected arguments", + expectedLines, actualLines); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalMergeToolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalMergeToolTest.java new file mode 100644 index 0000000000..94b67b374b --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalMergeToolTest.java @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2020-2022, Simeon Andreev <simeon.danailov.andreev@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.diffmergetool; + +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CMD; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_GUITOOL; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PATH; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TRUST_EXIT_CODE; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGETOOL_SECTION; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION; +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 static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import org.eclipse.jgit.lib.internal.BooleanTriState; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.util.FS.ExecutionResult; +import org.junit.Test; + +/** + * Testing external merge tools. + */ +public class ExternalMergeToolTest extends ExternalToolTestCase { + + @Test(expected = ToolException.class) + public void testUserToolWithError() throws Exception { + String toolName = "customTool"; + + int errorReturnCode = 1; + String command = "exit " + errorReturnCode; + + FileBasedConfig config = db.getConfig(); + config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD, + command); + config.setString(CONFIG_MERGETOOL_SECTION, toolName, + CONFIG_KEY_TRUST_EXIT_CODE, String.valueOf(Boolean.TRUE)); + + invokeMerge(toolName); + + fail("Expected exception to be thrown due to external tool exiting with error code: " + + errorReturnCode); + } + + @Test(expected = ToolException.class) + public void testUserToolWithCommandNotFoundError() throws Exception { + String toolName = "customTool"; + + int errorReturnCode = 127; // command not found + String command = "exit " + errorReturnCode; + + FileBasedConfig config = db.getConfig(); + config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD, + command); + + invokeMerge(toolName); + + fail("Expected exception to be thrown due to external tool exiting with error code: " + + errorReturnCode); + } + + @Test + public void testKdiff3() throws Exception { + assumePosixPlatform(); + + CommandLineMergeTool autoMergingTool = CommandLineMergeTool.kdiff3; + assumeMergeToolIsAvailable(autoMergingTool); + + CommandLineMergeTool tool = autoMergingTool; + PreDefinedMergeTool externalTool = new PreDefinedMergeTool(tool.name(), + tool.getPath(), tool.getParameters(true), + tool.getParameters(false), + tool.isExitCodeTrustable() ? BooleanTriState.TRUE + : BooleanTriState.FALSE); + + MergeTools manager = new MergeTools(db); + ExecutionResult result = manager.merge(local, remote, merged, null, + null, externalTool); + assertEquals("Expected merge tool to succeed", 0, result.getRc()); + + List<String> actualLines = Files.readAllLines(mergedFile.toPath()); + String actualMergeResult = String.join(System.lineSeparator(), + actualLines); + String expectedMergeResult = DEFAULT_CONTENT; + assertEquals( + "Failed to merge equal local and remote versions with pre-defined tool: " + + tool.getPath(), + expectedMergeResult, actualMergeResult); + } + + @Test + public void testUserDefinedTool() throws Exception { + String customToolName = "customTool"; + String command = getEchoCommand(); + + FileBasedConfig config = db.getConfig(); + config.setString(CONFIG_MERGETOOL_SECTION, customToolName, + CONFIG_KEY_CMD, command); + + MergeTools manager = new MergeTools(db); + Map<String, ExternalMergeTool> tools = manager.getUserDefinedTools(); + ExternalMergeTool externalTool = tools.get(customToolName); + manager.merge(local, remote, merged, base, null, externalTool); + + assertEchoCommandHasCorrectOutput(); + } + + @Test + public void testUserDefinedToolWithPrompt() throws Exception { + String customToolName = "customTool"; + String command = getEchoCommand(); + + FileBasedConfig config = db.getConfig(); + config.setString(CONFIG_MERGETOOL_SECTION, customToolName, + CONFIG_KEY_CMD, command); + + MergeTools manager = new MergeTools(db); + + PromptHandler promptHandler = PromptHandler.acceptPrompt(); + MissingToolHandler noToolHandler = new MissingToolHandler(); + + manager.merge(local, remote, merged, base, null, + Optional.of(customToolName), BooleanTriState.TRUE, false, + promptHandler, noToolHandler); + + assertEchoCommandHasCorrectOutput(); + + List<String> actualToolPrompts = promptHandler.toolPrompts; + List<String> expectedToolPrompts = Arrays.asList("customTool"); + assertEquals("Expected a user prompt for custom tool call", + expectedToolPrompts, actualToolPrompts); + + assertEquals("Expected to no informing about missing tools", + Collections.EMPTY_LIST, noToolHandler.missingTools); + } + + @Test + public void testUserDefinedToolWithCancelledPrompt() throws Exception { + MergeTools manager = new MergeTools(db); + + PromptHandler promptHandler = PromptHandler.cancelPrompt(); + MissingToolHandler noToolHandler = new MissingToolHandler(); + + Optional<ExecutionResult> result = manager.merge(local, remote, merged, + base, null, Optional.empty(), BooleanTriState.TRUE, false, + promptHandler, noToolHandler); + assertFalse("Expected no result if user cancels the operation", + result.isPresent()); + } + + @Test + public void testAllTools() { + FileBasedConfig config = db.getConfig(); + String customToolName = "customTool"; + config.setString(CONFIG_MERGETOOL_SECTION, customToolName, + CONFIG_KEY_CMD, "echo"); + + MergeTools manager = new MergeTools(db); + Set<String> actualToolNames = manager.getAllToolNames(); + Set<String> expectedToolNames = new LinkedHashSet<>(); + expectedToolNames.add(customToolName); + CommandLineMergeTool[] defaultTools = CommandLineMergeTool.values(); + for (CommandLineMergeTool defaultTool : defaultTools) { + String toolName = defaultTool.name(); + expectedToolNames.add(toolName); + } + assertEquals("Incorrect set of external merge tools", expectedToolNames, + actualToolNames); + } + + @Test + public void testOverridePredefinedToolPath() { + String toolName = CommandLineMergeTool.guiffy.name(); + String customToolPath = "/usr/bin/echo"; + + FileBasedConfig config = db.getConfig(); + config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD, + "echo"); + config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_PATH, + customToolPath); + + MergeTools manager = new MergeTools(db); + Map<String, ExternalMergeTool> tools = manager.getUserDefinedTools(); + ExternalMergeTool mergeTool = tools.get(toolName); + assertNotNull("Expected tool \"" + toolName + "\" to be user defined", + mergeTool); + + String toolPath = mergeTool.getPath(); + assertEquals("Expected external merge tool to have an overriden path", + customToolPath, toolPath); + } + + @Test + public void testUserDefinedTools() { + FileBasedConfig config = db.getConfig(); + String customToolname = "customTool"; + config.setString(CONFIG_MERGETOOL_SECTION, customToolname, + CONFIG_KEY_CMD, "echo"); + config.setString(CONFIG_MERGETOOL_SECTION, customToolname, + CONFIG_KEY_PATH, "/usr/bin/echo"); + config.setString(CONFIG_MERGETOOL_SECTION, customToolname, + CONFIG_KEY_PROMPT, String.valueOf(false)); + config.setString(CONFIG_MERGETOOL_SECTION, customToolname, + CONFIG_KEY_GUITOOL, String.valueOf(false)); + config.setString(CONFIG_MERGETOOL_SECTION, customToolname, + CONFIG_KEY_TRUST_EXIT_CODE, String.valueOf(false)); + MergeTools manager = new MergeTools(db); + Set<String> actualToolNames = manager.getUserDefinedTools().keySet(); + Set<String> expectedToolNames = new LinkedHashSet<>(); + expectedToolNames.add(customToolname); + assertEquals("Incorrect set of external merge tools", expectedToolNames, + actualToolNames); + } + + @Test + public void testCompare() throws ToolException { + String toolName = "customTool"; + + FileBasedConfig config = db.getConfig(); + // the default merge tool is configured without a subsection + String subsection = null; + config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_TOOL, + toolName); + + String command = getEchoCommand(); + + config.setString(CONFIG_MERGETOOL_SECTION, toolName, CONFIG_KEY_CMD, + command); + + Optional<ExecutionResult> result = invokeMerge(toolName); + assertTrue("Expected external merge tool result to be available", + result.isPresent()); + int expectedCompareResult = 0; + assertEquals("Incorrect compare result for external merge tool", + expectedCompareResult, result.get().getRc()); + } + + @Test + public void testDefaultTool() throws Exception { + String toolName = "customTool"; + String guiToolName = "customGuiTool"; + + FileBasedConfig config = db.getConfig(); + // the default merge tool is configured without a subsection + String subsection = null; + config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_TOOL, + toolName); + + MergeTools manager = new MergeTools(db); + boolean gui = false; + String defaultToolName = manager.getDefaultToolName(gui); + assertEquals( + "Expected configured mergetool to be the default external merge tool", + toolName, defaultToolName); + + gui = true; + String defaultGuiToolName = manager.getDefaultToolName(gui); + assertNull("Expected default mergetool to not be set", + defaultGuiToolName); + + config.setString(CONFIG_MERGE_SECTION, subsection, CONFIG_KEY_GUITOOL, + guiToolName); + manager = new MergeTools(db); + defaultGuiToolName = manager.getDefaultToolName(gui); + assertEquals( + "Expected configured mergetool to be the default external merge guitool", + guiToolName, defaultGuiToolName); + } + + @Test + public void testOverridePreDefinedToolPath() { + String newToolPath = "/tmp/path/"; + + CommandLineMergeTool[] defaultTools = CommandLineMergeTool.values(); + assertTrue("Expected to find pre-defined external merge tools", + defaultTools.length > 0); + + CommandLineMergeTool overridenTool = defaultTools[0]; + String overridenToolName = overridenTool.name(); + String overridenToolPath = newToolPath + overridenToolName; + FileBasedConfig config = db.getConfig(); + config.setString(CONFIG_MERGETOOL_SECTION, overridenToolName, + CONFIG_KEY_PATH, overridenToolPath); + + MergeTools manager = new MergeTools(db); + Map<String, ExternalMergeTool> availableTools = manager + .getPredefinedTools(true); + ExternalMergeTool externalMergeTool = availableTools + .get(overridenToolName); + String actualMergeToolPath = externalMergeTool.getPath(); + assertEquals( + "Expected pre-defined external merge tool to have overriden path", + overridenToolPath, actualMergeToolPath); + boolean withBase = true; + String expectedMergeToolCommand = overridenToolPath + " " + + overridenTool.getParameters(withBase); + String actualMergeToolCommand = externalMergeTool.getCommand(); + assertEquals( + "Expected pre-defined external merge tool to have overriden command", + expectedMergeToolCommand, actualMergeToolCommand); + } + + @Test(expected = ToolException.class) + public void testUndefinedTool() throws Exception { + String toolName = "undefined"; + invokeMerge(toolName); + fail("Expected exception to be thrown due to not defined external merge tool"); + } + + @Test + public void testDefaultToolExecutionWithPrompt() throws Exception { + FileBasedConfig config = db.getConfig(); + // the default diff tool is configured without a subsection + String subsection = null; + config.setString("merge", subsection, "tool", "customTool"); + + String command = getEchoCommand(); + + config.setString("mergetool", "customTool", "cmd", command); + + MergeTools manager = new MergeTools(db); + + PromptHandler promptHandler = PromptHandler.acceptPrompt(); + MissingToolHandler noToolHandler = new MissingToolHandler(); + + manager.merge(local, remote, merged, base, null, Optional.empty(), + BooleanTriState.TRUE, false, promptHandler, noToolHandler); + + assertEchoCommandHasCorrectOutput(); + } + + @Test + public void testNoDefaultToolName() { + MergeTools manager = new MergeTools(db); + boolean gui = false; + String defaultToolName = manager.getDefaultToolName(gui); + assertNull("Expected no default tool when none is configured", + defaultToolName); + + gui = true; + defaultToolName = manager.getDefaultToolName(gui); + assertNull("Expected no default tool when none is configured", + defaultToolName); + } + + @Test(expected = ToolException.class) + public void testNullTool() throws Exception { + MergeTools manager = new MergeTools(db); + + PromptHandler promptHandler = null; + MissingToolHandler noToolHandler = null; + + Optional<String> tool = null; + + manager.merge(local, remote, merged, base, null, tool, + BooleanTriState.TRUE, false, promptHandler, noToolHandler); + } + + @Test(expected = ToolException.class) + public void testNullToolWithPrompt() throws Exception { + MergeTools manager = new MergeTools(db); + + PromptHandler promptHandler = PromptHandler.cancelPrompt(); + MissingToolHandler noToolHandler = new MissingToolHandler(); + + Optional<String> tool = null; + + manager.merge(local, remote, merged, base, null, tool, + BooleanTriState.TRUE, false, promptHandler, noToolHandler); + } + + private Optional<ExecutionResult> invokeMerge(String toolName) + throws ToolException { + BooleanTriState prompt = BooleanTriState.UNSET; + boolean gui = false; + + MergeTools manager = new MergeTools(db); + + PromptHandler promptHandler = PromptHandler.acceptPrompt(); + MissingToolHandler noToolHandler = new MissingToolHandler(); + + Optional<ExecutionResult> result = manager.merge(local, remote, merged, + base, null, Optional.of(toolName), prompt, gui, promptHandler, + noToolHandler); + return result; + } + + private void assumeMergeToolIsAvailable( + CommandLineMergeTool autoMergingTool) { + boolean isAvailable = ExternalToolUtils.isToolAvailable(db.getFS(), + db.getDirectory(), db.getWorkTree(), autoMergingTool.getPath()); + assumeTrue("Assuming external tool is available: " + + autoMergingTool.name(), isAvailable); + } + + private String getEchoCommand() { + return "(echo $LOCAL $REMOTE $MERGED $BASE) > " + + commandResult.getAbsolutePath(); + } + + private void assertEchoCommandHasCorrectOutput() throws IOException { + List<String> actualLines = Files.readAllLines(commandResult.toPath()); + String actualContent = String.join(System.lineSeparator(), actualLines); + actualLines = Arrays.asList(actualContent.split(" ")); + List<String> expectedLines = Arrays.asList(localFile.getAbsolutePath(), + remoteFile.getAbsolutePath(), mergedFile.getAbsolutePath(), + baseFile.getAbsolutePath()); + assertEquals("Dummy test tool called with unexpected arguments", + expectedLines, actualLines); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalToolTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalToolTestCase.java index 0cc12978a8..7a6ff46578 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalToolTestCase.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalToolTestCase.java @@ -11,6 +11,8 @@ package org.eclipse.jgit.internal.diffmergetool; import java.io.File; import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.util.FS; @@ -36,6 +38,14 @@ public abstract class ExternalToolTestCase extends RepositoryTestCase { protected File commandResult; + protected FileElement local; + + protected FileElement remote; + + protected FileElement merged; + + protected FileElement base; + @Before @Override public void setUp() throws Exception { @@ -51,6 +61,15 @@ public abstract class ExternalToolTestCase extends RepositoryTestCase { baseFile.deleteOnExit(); commandResult = writeTrashFile("commandResult.txt", ""); commandResult.deleteOnExit(); + + local = new FileElement(localFile.getAbsolutePath(), + FileElement.Type.LOCAL); + remote = new FileElement(remoteFile.getAbsolutePath(), + FileElement.Type.REMOTE); + merged = new FileElement(mergedFile.getAbsolutePath(), + FileElement.Type.MERGED); + base = new FileElement(baseFile.getAbsolutePath(), + FileElement.Type.BASE); } @After @@ -71,4 +90,39 @@ public abstract class ExternalToolTestCase extends RepositoryTestCase { "This test can run only in Linux tests", FS.DETECTED instanceof FS_POSIX); } + + protected static class PromptHandler implements PromptContinueHandler { + + private final boolean promptResult; + + final List<String> toolPrompts = new ArrayList<>(); + + private PromptHandler(boolean promptResult) { + this.promptResult = promptResult; + } + + static PromptHandler acceptPrompt() { + return new PromptHandler(true); + } + + static PromptHandler cancelPrompt() { + return new PromptHandler(false); + } + + @Override + public boolean prompt(String toolName) { + toolPrompts.add(toolName); + return promptResult; + } + } + + protected static class MissingToolHandler implements InformNoToolHandler { + + final List<String> missingTools = new ArrayList<>(); + + @Override + public void inform(List<String> toolNames) { + missingTools.addAll(toolNames); + } + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitConfigTest.java index d95d7814e4..7066f9d422 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitConfigTest.java @@ -11,7 +11,10 @@ package org.eclipse.jgit.lib; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.CommitConfig.CleanupMode; @@ -169,6 +172,82 @@ public class CommitConfigTest { CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#')); } + @Test + public void testCommentCharDefault() throws Exception { + CommitConfig cfg = parse(""); + assertEquals('#', cfg.getCommentChar()); + assertFalse(cfg.isAutoCommentChar()); + } + + @Test + public void testCommentCharAuto() throws Exception { + CommitConfig cfg = parse("[core]\n\tcommentChar = auto\n"); + assertEquals('#', cfg.getCommentChar()); + assertTrue(cfg.isAutoCommentChar()); + } + + @Test + public void testCommentCharEmpty() throws Exception { + CommitConfig cfg = parse("[core]\n\tcommentChar =\n"); + assertEquals('#', cfg.getCommentChar()); + } + + @Test + public void testCommentCharInvalid() throws Exception { + CommitConfig cfg = parse("[core]\n\tcommentChar = \" \"\n"); + assertEquals('#', cfg.getCommentChar()); + } + + @Test + public void testCommentCharNonAscii() throws Exception { + CommitConfig cfg = parse("[core]\n\tcommentChar = ö\n"); + assertEquals('#', cfg.getCommentChar()); + } + + @Test + public void testCommentChar() throws Exception { + CommitConfig cfg = parse("[core]\n\tcommentChar = _\n"); + assertEquals('_', cfg.getCommentChar()); + } + + @Test + public void testDetermineCommentChar() throws Exception { + String text = "A commit message\n\nBody\n"; + assertEquals('#', CommitConfig.determineCommentChar(text)); + } + + @Test + public void testDetermineCommentChar2() throws Exception { + String text = "A commit message\n\nBody\n\n# Conflicts:\n#\tfoo.txt\n"; + char ch = CommitConfig.determineCommentChar(text); + assertNotEquals('#', ch); + assertTrue(ch > ' ' && ch < 127); + } + + @Test + public void testDetermineCommentChar3() throws Exception { + String text = "A commit message\n\n;Body\n\n# Conflicts:\n#\tfoo.txt\n"; + char ch = CommitConfig.determineCommentChar(text); + assertNotEquals('#', ch); + assertNotEquals(';', ch); + assertTrue(ch > ' ' && ch < 127); + } + + @Test + public void testDetermineCommentChar4() throws Exception { + String text = "A commit message\n\nBody\n\n # Conflicts:\n\t #\tfoo.txt\n"; + char ch = CommitConfig.determineCommentChar(text); + assertNotEquals('#', ch); + assertTrue(ch > ' ' && ch < 127); + } + + @Test + public void testDetermineCommentChar5() throws Exception { + String text = "A commit message\n\nBody\n\n#a\n;b\n@c\n!d\n$\n%\n^\n&\n|\n:"; + char ch = CommitConfig.determineCommentChar(text); + assertEquals(0, ch); + } + private static CommitConfig parse(String content) throws ConfigInvalidException { Config c = new Config(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java index b56308cb72..ef0817adb8 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java @@ -443,6 +443,26 @@ public class RefSpecTest { a.setDestination("refs/remotes/origin/*/*"); } + @Test(expected = IllegalArgumentException.class) + public void invalidNegativeAndForce() { + assertNotNull(new RefSpec("^+refs/heads/master")); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidForceAndNegative() { + assertNotNull(new RefSpec("+^refs/heads/master")); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidNegativeNoSrcDest() { + assertNotNull(new RefSpec("^")); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidNegativeBothSrcDest() { + assertNotNull(new RefSpec("^refs/heads/*:refs/heads/*")); + } + @Test public void sourceOnlywithWildcard() { RefSpec a = new RefSpec("refs/heads/*", @@ -480,4 +500,32 @@ public class RefSpecTest { assertTrue(a.isMatching()); assertTrue(a.isForceUpdate()); } + + @Test + public void negativeRefSpecWithDest() { + RefSpec a = new RefSpec("^:refs/readonly/*"); + assertTrue(a.isNegative()); + assertNull(a.getSource()); + assertEquals(a.getDestination(), "refs/readonly/*"); + } + + // Because of some of the API's existing behavior, without a colon at the + // end of the refspec, dest will be null. + @Test + public void negativeRefSpecWithSrcAndNullDest() { + RefSpec a = new RefSpec("^refs/testdata/*"); + assertTrue(a.isNegative()); + assertNull(a.getDestination()); + assertEquals(a.getSource(), "refs/testdata/*"); + } + + // Because of some of the API's existing behavior, with a colon at the end + // of the refspec, dest will be empty. + @Test + public void negativeRefSpecWithSrcAndEmptyDest() { + RefSpec a = new RefSpec("^refs/testdata/*:"); + assertTrue(a.isNegative()); + assertTrue(a.getDestination().isEmpty()); + assertEquals(a.getSource(), "refs/testdata/*"); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandInputStreamTest.java new file mode 100644 index 0000000000..7ac83195fb --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandInputStreamTest.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.transport; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; + +import org.junit.Before; +import org.junit.Test; + +public class SideBandInputStreamTest { + + private StringWriter messages; + + private SideBandInputStream sideband; + + @Before + public void setup() { + messages = new StringWriter(); + } + + @Test + public void progressSingleCR() throws IOException { + init(packet("message\r")); + assertTrue(sideband.read() < 0); + assertEquals("message\r", messages.toString()); + } + + @Test + public void progressSingleLF() throws IOException { + init(packet("message\n")); + assertTrue(sideband.read() < 0); + assertEquals("message\n", messages.toString()); + } + + @Test + public void progressSingleCRLF() throws IOException { + init(packet("message\r\n")); + assertTrue(sideband.read() < 0); + assertEquals("message\r\n", messages.toString()); + } + + @Test + public void progressMultiCR() throws IOException { + init(packet("message 0%\rmessage 100%\r")); + assertTrue(sideband.read() < 0); + assertEquals("message 0%\rmessage 100%\r", messages.toString()); + } + + @Test + public void progressMultiLF() throws IOException { + init(packet("message 0%\nmessage 100%\n")); + assertTrue(sideband.read() < 0); + assertEquals("message 0%\nmessage 100%\n", messages.toString()); + } + + @Test + public void progressMultiCRLF() throws IOException { + init(packet("message 0%\r\nmessage 100%\r\n")); + assertTrue(sideband.read() < 0); + assertEquals("message 0%\r\nmessage 100%\r\n", messages.toString()); + } + + @Test + public void progressPartial() throws IOException { + init(packet("message")); + assertTrue(sideband.read() < 0); + assertEquals("", messages.toString()); + sideband.drainMessages(); + assertEquals("message\n", messages.toString()); + } + + @Test + public void progressPartialTwoCR() throws IOException { + init(packet("message") + packet("message\r")); + assertTrue(sideband.read() < 0); + assertEquals("messagemessage\r", messages.toString()); + } + + @Test + public void progressPartialTwoLF() throws IOException { + init(packet("message") + packet("message\n")); + assertTrue(sideband.read() < 0); + assertEquals("messagemessage\n", messages.toString()); + } + + @Test + public void progressPartialTwoCRLF() throws IOException { + init(packet("message") + packet("message\r\n")); + assertTrue(sideband.read() < 0); + assertEquals("messagemessage\r\n", messages.toString()); + } + + @Test + public void progressPartialThreeCR() throws IOException { + init(packet("message") + packet("message") + packet("message\r")); + assertTrue(sideband.read() < 0); + assertEquals("messagemessagemessage\r", messages.toString()); + } + + @Test + public void progressPartialThreeLF() throws IOException { + init(packet("message") + packet("message") + packet("message\n")); + assertTrue(sideband.read() < 0); + assertEquals("messagemessagemessage\n", messages.toString()); + } + + @Test + public void progressPartialThreeCRLF() throws IOException { + init(packet("message") + packet("message") + packet("message\r\n")); + assertTrue(sideband.read() < 0); + assertEquals("messagemessagemessage\r\n", messages.toString()); + } + + @Test + public void progressPartialCR() throws IOException { + init(packet("message 0%\rmessage 100%")); + assertTrue(sideband.read() < 0); + assertEquals("message 0%\r", messages.toString()); + sideband.drainMessages(); + assertEquals("message 0%\rmessage 100%\n", messages.toString()); + } + + @Test + public void progressPartialLF() throws IOException { + init(packet("message 0%\nmessage 100%")); + assertTrue(sideband.read() < 0); + assertEquals("message 0%\n", messages.toString()); + sideband.drainMessages(); + assertEquals("message 0%\nmessage 100%\n", messages.toString()); + } + + @Test + public void progressPartialCRLF() throws IOException { + init(packet("message 0%\r\nmessage 100%")); + assertTrue(sideband.read() < 0); + assertEquals("message 0%\r\n", messages.toString()); + sideband.drainMessages(); + assertEquals("message 0%\r\nmessage 100%\n", messages.toString()); + } + + @Test + public void progressPartialSplitCR() throws IOException { + init(packet("message") + "0006\001a" + packet(" 0%\rmessa") + + packet("ge 100%")); + assertEquals('a', sideband.read()); + assertEquals("", messages.toString()); + assertTrue(sideband.read() < 0); + assertEquals("message 0%\r", messages.toString()); + sideband.drainMessages(); + assertEquals("message 0%\rmessage 100%\n", messages.toString()); + } + + @Test + public void progressPartialSplitLF() throws IOException { + init(packet("message") + "0006\001a" + packet(" 0%\nmessa") + + packet("ge 100%")); + assertEquals('a', sideband.read()); + assertEquals("", messages.toString()); + assertTrue(sideband.read() < 0); + assertEquals("message 0%\n", messages.toString()); + sideband.drainMessages(); + assertEquals("message 0%\nmessage 100%\n", messages.toString()); + } + + @Test + public void progressPartialSplitCRLF() throws IOException { + init(packet("message") + "0006\001a" + packet(" 0%\r\nmessa") + + packet("ge 100%")); + assertEquals('a', sideband.read()); + assertEquals("", messages.toString()); + assertTrue(sideband.read() < 0); + assertEquals("message 0%\r\n", messages.toString()); + sideband.drainMessages(); + assertEquals("message 0%\r\nmessage 100%\n", messages.toString()); + } + + @Test + public void progressInterleaved() throws IOException { + init(packet("message 0%\r") + "0006\001a" + packet("message 10%") + + "0006\001b" + packet("\rmessage 100%\n")); + assertEquals('a', sideband.read()); + assertEquals("message 0%\r", messages.toString()); + assertEquals('b', sideband.read()); + assertEquals("message 0%\r", messages.toString()); + assertTrue(sideband.read() < 0); + assertEquals("message 0%\rmessage 10%\rmessage 100%\n", + messages.toString()); + } + + @Test + public void progressInterleavedPartial() throws IOException { + init(packet("message 0%\r") + "0006\001a" + packet("message 10%") + + "0006\001b" + packet("\rmessage 100%")); + assertEquals('a', sideband.read()); + assertEquals("message 0%\r", messages.toString()); + assertEquals('b', sideband.read()); + assertEquals("message 0%\r", messages.toString()); + assertTrue(sideband.read() < 0); + assertEquals("message 0%\rmessage 10%\r", messages.toString()); + sideband.drainMessages(); + assertEquals("message 0%\rmessage 10%\rmessage 100%\n", + messages.toString()); + } + + private String packet(String data) { + return String.format("%04x\002%s", Integer.valueOf(data.length() + 5), + data); + } + + private void init(String packets) { + InputStream rawIn = new ByteArrayInputStream( + (packets + "0000").getBytes(StandardCharsets.UTF_8)); + sideband = new SideBandInputStream(rawIn, null, messages, null); + } +} diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF index 7baa215801..e9c587993b 100644 --- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF @@ -4,14 +4,14 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.ui Bundle-SymbolicName: org.eclipse.jgit.ui -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-RequiredExecutionEnvironment: JavaSE-11 -Export-Package: org.eclipse.jgit.awtui;version="6.1.1" -Import-Package: org.eclipse.jgit.errors;version="[6.1.1,6.2.0)", - org.eclipse.jgit.lib;version="[6.1.1,6.2.0)", - org.eclipse.jgit.nls;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revplot;version="[6.1.1,6.2.0)", - org.eclipse.jgit.revwalk;version="[6.1.1,6.2.0)", - org.eclipse.jgit.transport;version="[6.1.1,6.2.0)", - org.eclipse.jgit.util;version="[6.1.1,6.2.0)" +Export-Package: org.eclipse.jgit.awtui;version="6.2.0" +Import-Package: org.eclipse.jgit.errors;version="[6.2.0,6.3.0)", + org.eclipse.jgit.lib;version="[6.2.0,6.3.0)", + org.eclipse.jgit.nls;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revplot;version="[6.2.0,6.3.0)", + org.eclipse.jgit.revwalk;version="[6.2.0,6.3.0)", + org.eclipse.jgit.transport;version="[6.2.0,6.3.0)", + org.eclipse.jgit.util;version="[6.2.0,6.3.0)" diff --git a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF index 75f3d217ab..df93dc2133 100644 --- a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.ui - Sources Bundle-SymbolicName: org.eclipse.jgit.ui.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ui;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ui;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml index 125e120b45..31c7149e84 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ui</artifactId> diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index eb8eb8bd65..a25685073d 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -3,12 +3,12 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit Bundle-SymbolicName: org.eclipse.jgit -Bundle-Version: 6.1.1.qualifier +Bundle-Version: 6.2.0.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor Eclipse-ExtensibleAPI: true -Export-Package: org.eclipse.jgit.annotations;version="6.1.1", - org.eclipse.jgit.api;version="6.1.1"; +Export-Package: org.eclipse.jgit.annotations;version="6.2.0", + org.eclipse.jgit.api;version="6.2.0"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.notes, org.eclipse.jgit.dircache, @@ -23,18 +23,18 @@ Export-Package: org.eclipse.jgit.annotations;version="6.1.1", org.eclipse.jgit.revwalk.filter, org.eclipse.jgit.blame, org.eclipse.jgit.merge", - org.eclipse.jgit.api.errors;version="6.1.1"; + org.eclipse.jgit.api.errors;version="6.2.0"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.errors", - org.eclipse.jgit.attributes;version="6.1.1"; + org.eclipse.jgit.attributes;version="6.2.0"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk", - org.eclipse.jgit.blame;version="6.1.1"; + org.eclipse.jgit.blame;version="6.2.0"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff", - org.eclipse.jgit.diff;version="6.1.1"; + org.eclipse.jgit.diff;version="6.2.0"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.attributes, org.eclipse.jgit.revwalk, @@ -42,48 +42,49 @@ Export-Package: org.eclipse.jgit.annotations;version="6.1.1", org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.treewalk, org.eclipse.jgit.util", - org.eclipse.jgit.dircache;version="6.1.1"; + org.eclipse.jgit.dircache;version="6.2.0"; uses:="org.eclipse.jgit.events, org.eclipse.jgit.lib, org.eclipse.jgit.attributes, org.eclipse.jgit.treewalk, org.eclipse.jgit.util", - org.eclipse.jgit.errors;version="6.1.1"; + org.eclipse.jgit.errors;version="6.2.0"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.internal.storage.pack", - org.eclipse.jgit.events;version="6.1.1"; + org.eclipse.jgit.events;version="6.2.0"; uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.fnmatch;version="6.1.1", - org.eclipse.jgit.gitrepo;version="6.1.1"; + org.eclipse.jgit.fnmatch;version="6.2.0", + org.eclipse.jgit.gitrepo;version="6.2.0"; 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="6.1.1";x-internal:=true, - org.eclipse.jgit.hooks;version="6.1.1";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.ignore;version="6.1.1", - org.eclipse.jgit.ignore.internal;version="6.1.1"; + org.eclipse.jgit.gitrepo.internal;version="6.2.0";x-internal:=true, + org.eclipse.jgit.hooks;version="6.2.0";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.ignore;version="6.2.0", + org.eclipse.jgit.ignore.internal;version="6.2.0"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal;version="6.1.1"; + org.eclipse.jgit.internal;version="6.2.0"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.http.test", - org.eclipse.jgit.internal.diffmergetool;version="6.1.1"; + org.eclipse.jgit.internal.diffmergetool;version="6.2.0"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.pgm.test, - org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.fsck;version="6.1.1"; + org.eclipse.jgit.pgm, + org.eclipse.egit.ui", + org.eclipse.jgit.internal.fsck;version="6.2.0"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.revwalk;version="6.1.1"; + org.eclipse.jgit.internal.revwalk;version="6.2.0"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.storage.dfs;version="6.1.1"; + org.eclipse.jgit.internal.storage.dfs;version="6.2.0"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.http.server, org.eclipse.jgit.http.test, org.eclipse.jgit.lfs.test", - org.eclipse.jgit.internal.storage.file;version="6.1.1"; + org.eclipse.jgit.internal.storage.file;version="6.2.0"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.junit, org.eclipse.jgit.junit.http, @@ -92,32 +93,32 @@ Export-Package: org.eclipse.jgit.annotations;version="6.1.1", org.eclipse.jgit.pgm, org.eclipse.jgit.pgm.test, org.eclipse.jgit.ssh.apache", - org.eclipse.jgit.internal.storage.io;version="6.1.1"; + org.eclipse.jgit.internal.storage.io;version="6.2.0"; x-friends:="org.eclipse.jgit.junit, org.eclipse.jgit.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.pack;version="6.1.1"; + org.eclipse.jgit.internal.storage.pack;version="6.2.0"; x-friends:="org.eclipse.jgit.junit, org.eclipse.jgit.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.reftable;version="6.1.1"; + org.eclipse.jgit.internal.storage.reftable;version="6.2.0"; x-friends:="org.eclipse.jgit.http.test, org.eclipse.jgit.junit, org.eclipse.jgit.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.submodule;version="6.1.1";x-internal:=true, - org.eclipse.jgit.internal.transport.connectivity;version="6.1.1"; + org.eclipse.jgit.internal.submodule;version="6.2.0";x-internal:=true, + org.eclipse.jgit.internal.transport.connectivity;version="6.2.0"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.http;version="6.1.1"; + org.eclipse.jgit.internal.transport.http;version="6.2.0"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.parser;version="6.1.1"; + org.eclipse.jgit.internal.transport.parser;version="6.2.0"; x-friends:="org.eclipse.jgit.http.server, org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.ssh;version="6.1.1"; + org.eclipse.jgit.internal.transport.ssh;version="6.2.0"; x-friends:="org.eclipse.jgit.ssh.apache, org.eclipse.jgit.ssh.jsch, org.eclipse.jgit.test", - org.eclipse.jgit.lib;version="6.1.1"; + org.eclipse.jgit.lib;version="6.2.0"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util.sha1, org.eclipse.jgit.dircache, @@ -131,11 +132,12 @@ Export-Package: org.eclipse.jgit.annotations;version="6.1.1", org.eclipse.jgit.util, org.eclipse.jgit.submodule, org.eclipse.jgit.util.time", - org.eclipse.jgit.lib.internal;version="6.1.1"; + org.eclipse.jgit.lib.internal;version="6.2.0"; x-friends:="org.eclipse.jgit.test, - org.eclipse.jgit.pgm", - org.eclipse.jgit.logging;version="6.1.1", - org.eclipse.jgit.merge;version="6.1.1"; + org.eclipse.jgit.pgm, + org.eclipse.egit.ui", + org.eclipse.jgit.logging;version="6.2.0", + org.eclipse.jgit.merge;version="6.2.0"; uses:="org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, @@ -144,40 +146,40 @@ Export-Package: org.eclipse.jgit.annotations;version="6.1.1", org.eclipse.jgit.util, org.eclipse.jgit.api, org.eclipse.jgit.attributes", - org.eclipse.jgit.nls;version="6.1.1", - org.eclipse.jgit.notes;version="6.1.1"; + org.eclipse.jgit.nls;version="6.2.0", + org.eclipse.jgit.notes;version="6.2.0"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk, org.eclipse.jgit.merge", - org.eclipse.jgit.patch;version="6.1.1"; + org.eclipse.jgit.patch;version="6.2.0"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.diff", - org.eclipse.jgit.revplot;version="6.1.1"; + org.eclipse.jgit.revplot;version="6.2.0"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk", - org.eclipse.jgit.revwalk;version="6.1.1"; + org.eclipse.jgit.revwalk;version="6.2.0"; 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="6.1.1"; + org.eclipse.jgit.revwalk.filter;version="6.2.0"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.lib, org.eclipse.jgit.util", - org.eclipse.jgit.storage.file;version="6.1.1"; + org.eclipse.jgit.storage.file;version="6.2.0"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.util", - org.eclipse.jgit.storage.pack;version="6.1.1"; + org.eclipse.jgit.storage.pack;version="6.2.0"; uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.submodule;version="6.1.1"; + org.eclipse.jgit.submodule;version="6.2.0"; 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="6.1.1"; + org.eclipse.jgit.transport;version="6.2.0"; uses:="javax.crypto, org.eclipse.jgit.util.io, org.eclipse.jgit.lib, @@ -190,21 +192,21 @@ Export-Package: org.eclipse.jgit.annotations;version="6.1.1", org.eclipse.jgit.transport.resolver, org.eclipse.jgit.storage.pack, org.eclipse.jgit.errors", - org.eclipse.jgit.transport.http;version="6.1.1"; + org.eclipse.jgit.transport.http;version="6.2.0"; uses:="javax.net.ssl", - org.eclipse.jgit.transport.resolver;version="6.1.1"; + org.eclipse.jgit.transport.resolver;version="6.2.0"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.lib", - org.eclipse.jgit.treewalk;version="6.1.1"; + org.eclipse.jgit.treewalk;version="6.2.0"; uses:="org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.attributes, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util", - org.eclipse.jgit.treewalk.filter;version="6.1.1"; + org.eclipse.jgit.treewalk.filter;version="6.2.0"; uses:="org.eclipse.jgit.treewalk", - org.eclipse.jgit.util;version="6.1.1"; + org.eclipse.jgit.util;version="6.2.0"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.hooks, org.eclipse.jgit.revwalk, @@ -217,12 +219,12 @@ Export-Package: org.eclipse.jgit.annotations;version="6.1.1", org.eclipse.jgit.treewalk, javax.net.ssl, org.eclipse.jgit.util.time", - org.eclipse.jgit.util.io;version="6.1.1"; + org.eclipse.jgit.util.io;version="6.2.0"; uses:="org.eclipse.jgit.attributes, org.eclipse.jgit.lib, org.eclipse.jgit.treewalk", - org.eclipse.jgit.util.sha1;version="6.1.1", - org.eclipse.jgit.util.time;version="6.1.1" + org.eclipse.jgit.util.sha1;version="6.2.0", + org.eclipse.jgit.util.time;version="6.2.0" Bundle-RequiredExecutionEnvironment: JavaSE-11 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 33e6156773..a418bc360d 100644 --- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit - Sources Bundle-SymbolicName: org.eclipse.jgit.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 6.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit;version="6.1.1.qualifier";roots="." +Bundle-Version: 6.2.0.qualifier +Eclipse-SourceBundle: org.eclipse.jgit;version="6.2.0.qualifier";roots="." diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index f0e92b3894..2fbec2fbf1 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>6.1.1-SNAPSHOT</version> + <version>6.2.0-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 e6f4e65e70..f0bb6c6c99 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -237,6 +237,9 @@ deleteTagUnexpectedResult=Delete tag returned unexpected result {0} deletingNotSupported=Deleting {0} not supported. destinationIsNotAWildcard=Destination is not a wildcard. detachedHeadDetected=HEAD is detached +diffToolNotGivenError=No diff tool provided and no defaults configured. +diffToolNotSpecifiedInGitAttributesError=Diff tool specified in git attributes cannot be found. +diffToolNullError=Parameter for diff tool cannot be null. dirCacheDoesNotHaveABackingFile=DirCache does not have a backing file dirCacheFileIsNotLocked=DirCache {0} not locked dirCacheIsNotLocked=DirCache is not locked @@ -393,6 +396,7 @@ invalidLineInConfigFileWithParam=Invalid line in config file: {0} invalidModeFor=Invalid mode {0} for {1} {2} in {3}. invalidModeForPath=Invalid mode {0} for path {1} invalidNameContainsDotDot=Invalid name (contains ".."): {0} +invalidNegativeAndForce= RefSpec can't be negative and forceful. invalidObject=Invalid {0} {1}: {2} invalidOldIdSent=invalid old id sent invalidPacketLineHeader=Invalid packet line header: {0} @@ -456,6 +460,8 @@ mergeStrategyDoesNotSupportHeads=merge strategy {0} does not support {1} heads t 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} 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. messageAndTaggerNotAllowedInUnannotatedTags = Unannotated tags cannot have a message or tagger minutesAgo={0} minutes ago mismatchOffset=mismatch offset for object {0} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java index f88179ac1a..ceba89d166 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java @@ -30,6 +30,7 @@ import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.events.WorkingTreeModifiedEvent; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.CommitConfig; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; @@ -183,9 +184,13 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> { String message; if (unmergedPaths != null) { + CommitConfig cfg = repo.getConfig() + .get(CommitConfig.KEY); + message = srcCommit.getFullMessage(); + char commentChar = cfg.getCommentChar(message); message = new MergeMessageFormatter() - .formatWithConflicts(srcCommit.getFullMessage(), - unmergedPaths, '#'); + .formatWithConflicts(message, unmergedPaths, + commentChar); } else { message = srcCommit.getFullMessage(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java index 7a591aa3b5..3b3baf5a12 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -233,11 +233,25 @@ public class CommitCommand extends GitCommand<RevCommit> { config = repo.getConfig().get(CommitConfig.KEY); cleanupMode = config.resolve(cleanupMode, cleanDefaultIsStrip); } - char comments; - if (commentChar == null) { - comments = '#'; // TODO use git config core.commentChar - } else { - comments = commentChar.charValue(); + char comments = (char) 0; + if (CleanupMode.STRIP.equals(cleanupMode) + || CleanupMode.SCISSORS.equals(cleanupMode)) { + if (commentChar == null) { + if (config == null) { + config = repo.getConfig().get(CommitConfig.KEY); + } + if (config.isAutoCommentChar()) { + // We're supposed to pick a character that isn't used, + // but then cleaning up won't remove any lines. So don't + // bother. + comments = (char) 0; + cleanupMode = CleanupMode.WHITESPACE; + } else { + comments = config.getCommentChar(); + } + } else { + comments = commentChar.charValue(); + } } message = CommitConfig.cleanText(message, cleanupMode, comments); @@ -309,8 +323,14 @@ public class CommitCommand extends GitCommand<RevCommit> { private void sign(CommitBuilder commit) throws ServiceUnavailableException, CanceledException, UnsupportedSigningFormatException { if (gpgSigner == null) { - throw new ServiceUnavailableException( - JGitText.get().signingServiceUnavailable); + gpgSigner = GpgSigner.getDefault(); + if (gpgSigner == null) { + throw new ServiceUnavailableException( + JGitText.get().signingServiceUnavailable); + } + } + if (signingKey == null) { + signingKey = gpgConfig.getSigningKey(); } if (gpgSigner instanceof GpgObjectSigner) { ((GpgObjectSigner) gpgSigner).signObject(commit, @@ -645,12 +665,6 @@ public class CommitCommand extends GitCommand<RevCommit> { signCommit = gpgConfig.isSignCommits() ? Boolean.TRUE : Boolean.FALSE; } - if (signingKey == null) { - signingKey = gpgConfig.getSigningKey(); - } - if (gpgSigner == null) { - gpgSigner = GpgSigner.getDefault(); - } } private boolean isMergeDuringRebase(RepositoryState state) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java index 0c691062f9..c3415581ef 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2020 Christoph Brill <egore911@egore911.de> and others + * Copyright (C) 2011, 2022 Christoph Brill <egore911@egore911.de> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -9,6 +9,7 @@ */ package org.eclipse.jgit.api; +import java.io.IOException; import java.net.URISyntaxException; import java.text.MessageFormat; import java.util.ArrayList; @@ -20,8 +21,8 @@ import java.util.Map; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRemoteException; import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.NotSupportedException; -import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; @@ -30,6 +31,8 @@ import org.eclipse.jgit.transport.FetchConnection; import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.Transport; import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.transport.UrlConfig; +import org.eclipse.jgit.util.SystemReader; /** * The ls-remote command @@ -153,7 +156,7 @@ public class LsRemoteCommand extends try (Transport transport = repo != null ? Transport.open(repo, remote) - : Transport.open(new URIish(remote))) { + : Transport.open(new URIish(translate(remote)))) { transport.setOptionUploadPack(uploadPack); configure(transport); Collection<RefSpec> refSpecs = new ArrayList<>(1); @@ -185,11 +188,16 @@ public class LsRemoteCommand extends throw new JGitInternalException( JGitText.get().exceptionCaughtDuringExecutionOfLsRemoteCommand, e); - } catch (TransportException e) { + } catch (IOException | ConfigInvalidException e) { throw new org.eclipse.jgit.api.errors.TransportException( - e.getMessage(), - e); + e.getMessage(), e); } } + private String translate(String uri) + throws IOException, ConfigInvalidException { + UrlConfig urls = new UrlConfig( + SystemReader.getInstance().getUserConfig()); + return urls.replace(uri); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java index ce068b6306..ed4a5342b3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java @@ -34,6 +34,7 @@ import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.events.WorkingTreeModifiedEvent; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.CommitConfig; import org.eclipse.jgit.lib.Config.ConfigEnum; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; @@ -404,8 +405,11 @@ public class MergeCommand extends GitCommand<MergeResult> { MergeStatus.FAILED, mergeStrategy, lowLevelResults, failingPaths, null); } + CommitConfig cfg = repo.getConfig().get(CommitConfig.KEY); + char commentChar = cfg.getCommentChar(message); String mergeMessageWithConflicts = new MergeMessageFormatter() - .formatWithConflicts(mergeMessage, unmergedPaths, '#'); + .formatWithConflicts(mergeMessage, unmergedPaths, + commentChar); repo.writeMergeCommitMsg(mergeMessageWithConflicts); return new MergeResult(null, merger.getBaseCommitId(), new ObjectId[] { headCommit.getId(), diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java index 2b0d8ce1c9..4e0d9d78c3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -449,7 +449,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> { String oldMessage = commitToPick.getFullMessage(); CleanupMode mode = commitConfig.resolve(CleanupMode.DEFAULT, true); boolean[] doChangeId = { false }; - String newMessage = editCommitMessage(doChangeId, oldMessage, mode); + String newMessage = editCommitMessage(doChangeId, oldMessage, mode, + commitConfig.getCommentChar(oldMessage)); try (Git git = new Git(repo)) { newHead = git.commit() .setMessage(newMessage) @@ -494,12 +495,12 @@ public class RebaseCommand extends GitCommand<RebaseResult> { } private String editCommitMessage(boolean[] doChangeId, String message, - @NonNull CleanupMode mode) { + @NonNull CleanupMode mode, char commentChar) { String newMessage; CommitConfig.CleanupMode cleanup; if (interactiveHandler instanceof InteractiveHandler2) { InteractiveHandler2.ModifyResult modification = ((InteractiveHandler2) interactiveHandler) - .editCommitMessage(message, mode, '#'); + .editCommitMessage(message, mode, commentChar); newMessage = modification.getMessage(); cleanup = modification.getCleanupMode(); if (CleanupMode.DEFAULT.equals(cleanup)) { @@ -511,7 +512,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { cleanup = CommitConfig.CleanupMode.STRIP; doChangeId[0] = false; } - return CommitConfig.cleanText(newMessage, cleanup, '#'); + return CommitConfig.cleanText(newMessage, cleanup, commentChar); } private RebaseResult cherryPickCommit(RevCommit commitToPick) @@ -808,8 +809,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> { if (isLast) { boolean[] doChangeId = { false }; if (sequenceContainsSquash) { + char commentChar = commitMessage.charAt(0); commitMessage = editCommitMessage(doChangeId, commitMessage, - CleanupMode.STRIP); + CleanupMode.STRIP, commentChar); } retNewHead = git.commit() .setMessage(commitMessage) @@ -829,30 +831,60 @@ public class RebaseCommand extends GitCommand<RebaseResult> { } @SuppressWarnings("nls") - private static String composeSquashMessage(boolean isSquash, + private String composeSquashMessage(boolean isSquash, RevCommit commitToPick, String currSquashMessage, int count) { StringBuilder sb = new StringBuilder(); String ordinal = getOrdinal(count); - sb.setLength(0); - sb.append("# This is a combination of ").append(count) - .append(" commits.\n"); - // Add the previous message without header (i.e first line) - sb.append(currSquashMessage - .substring(currSquashMessage.indexOf('\n') + 1)); - sb.append("\n"); - if (isSquash) { - sb.append("# This is the ").append(count).append(ordinal) - .append(" commit message:\n"); - sb.append(commitToPick.getFullMessage()); + // currSquashMessage is always non-empty here, and the first character + // is the comment character used so far. + char commentChar = currSquashMessage.charAt(0); + String newMessage = commitToPick.getFullMessage(); + if (!isSquash) { + sb.append(commentChar).append(" This is a combination of ") + .append(count).append(" commits.\n"); + // Add the previous message without header (i.e first line) + sb.append(currSquashMessage + .substring(currSquashMessage.indexOf('\n') + 1)); + sb.append('\n'); + sb.append(commentChar).append(" The ").append(count).append(ordinal) + .append(" commit message will be skipped:\n") + .append(commentChar).append(' '); + sb.append(newMessage.replaceAll("([\n\r])", + "$1" + commentChar + ' ')); } else { - sb.append("# The ").append(count).append(ordinal) - .append(" commit message will be skipped:\n# "); - sb.append(commitToPick.getFullMessage().replaceAll("([\n\r])", - "$1# ")); + String currentMessage = currSquashMessage; + if (commitConfig.isAutoCommentChar()) { + // Figure out a new comment character taking into account the + // new message + String cleaned = CommitConfig.cleanText(currentMessage, + CommitConfig.CleanupMode.STRIP, commentChar) + '\n' + + newMessage; + char newCommentChar = commitConfig.getCommentChar(cleaned); + if (newCommentChar != commentChar) { + currentMessage = replaceCommentChar(currentMessage, + commentChar, newCommentChar); + commentChar = newCommentChar; + } + } + sb.append(commentChar).append(" This is a combination of ") + .append(count).append(" commits.\n"); + // Add the previous message without header (i.e first line) + sb.append( + currentMessage.substring(currentMessage.indexOf('\n') + 1)); + sb.append('\n'); + sb.append(commentChar).append(" This is the ").append(count) + .append(ordinal).append(" commit message:\n"); + sb.append(newMessage); } return sb.toString(); } + private String replaceCommentChar(String message, char oldChar, + char newChar) { + // (?m) - Switch on multi-line matching; \h - horizontal whitespace + return message.replaceAll("(?m)^(\\h*)" + oldChar, "$1" + newChar); //$NON-NLS-1$ //$NON-NLS-2$ + } + private static String getOrdinal(int count) { switch (count % 10) { case 1: @@ -886,10 +918,11 @@ public class RebaseCommand extends GitCommand<RebaseResult> { private void initializeSquashFixupFile(String messageFile, String fullMessage) throws IOException { - rebaseState - .createFile( - messageFile, - "# This is a combination of 1 commits.\n# The first commit's message is:\n" + fullMessage); //$NON-NLS-1$); + char commentChar = commitConfig.getCommentChar(fullMessage); + rebaseState.createFile(messageFile, + commentChar + " This is a combination of 1 commits.\n" //$NON-NLS-1$ + + commentChar + " The first commit's message is:\n" //$NON-NLS-1$ + + fullMessage); } private String getOurCommitName() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java index db88ad8dc9..513f579b67 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java @@ -30,6 +30,7 @@ import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.events.WorkingTreeModifiedEvent; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.CommitConfig; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; @@ -185,9 +186,12 @@ public class RevertCommand extends GitCommand<RevCommit> { MergeStatus.CONFLICTING, strategy, merger.getMergeResults(), failingPaths, null); if (!merger.failed() && !unmergedPaths.isEmpty()) { + CommitConfig config = repo.getConfig() + .get(CommitConfig.KEY); + char commentChar = config.getCommentChar(newMessage); String message = new MergeMessageFormatter() .formatWithConflicts(newMessage, - merger.getUnmergedPaths(), '#'); + merger.getUnmergedPaths(), commentChar); repo.writeRevertHead(srcCommit.getId()); repo.writeMergeCommitMsg(message); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java index 1a41df3d0a..64ff19c9c3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, 2020 Google Inc. and others + * Copyright (C) 2010, 2021 Google Inc. and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -91,6 +91,29 @@ public abstract class ContentSource { public abstract ObjectLoader open(String path, ObjectId id) throws IOException; + /** + * Closes the used resources like ObjectReader, TreeWalk etc. Default + * implementation does nothing. + * + * @since 6.2 + */ + public void close() { + // Do nothing + } + + /** + * Checks if the source is from "working tree", so it can be accessed as a + * file directly. + * + * @since 6.2 + * + * @return true if working tree source and false otherwise (loader must be + * used) + */ + public boolean isWorkingTreeSource() { + return false; + } + private static class ObjectReaderSource extends ContentSource { private final ObjectReader reader; @@ -111,6 +134,16 @@ public abstract class ContentSource { public ObjectLoader open(String path, ObjectId id) throws IOException { return reader.open(id, Constants.OBJ_BLOB); } + + @Override + public void close() { + reader.close(); + } + + @Override + public boolean isWorkingTreeSource() { + return false; + } } private static class WorkingTreeSource extends ContentSource { @@ -194,6 +227,16 @@ public abstract class ContentSource { throw new FileNotFoundException(path); } } + + @Override + public void close() { + tw.close(); + } + + @Override + public boolean isWorkingTreeSource() { + return true; + } } /** A pair of sources to access the old and new sides of a DiffEntry. */ @@ -261,5 +304,37 @@ public abstract class ContentSource { throw new IllegalArgumentException(); } } + + /** + * Closes used resources. + * + * @since 6.2 + */ + public void close() { + oldSource.close(); + newSource.close(); + } + + /** + * Checks if source (side) is a "working tree". + * + * @since 6.2 + * + * @param side + * which side of the entry to read (OLD or NEW). + * @return is the source a "working tree" + * + */ + public boolean isWorkingTreeSource(DiffEntry.Side side) { + switch (side) { + case OLD: + return oldSource.isWorkingTreeSource(); + case NEW: + return newSource.isWorkingTreeSource(); + default: + throw new IllegalArgumentException(); + } + } + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java index 3d50a82155..f6fc393c45 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -26,9 +26,9 @@ import java.nio.file.StandardCopyOption; import java.text.MessageFormat; import java.time.Instant; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -113,7 +113,7 @@ public class DirCacheCheckout { private Repository repo; - private HashMap<String, CheckoutMetadata> updated = new HashMap<>(); + private Map<String, CheckoutMetadata> updated = new LinkedHashMap<>(); private ArrayList<String> conflicts = new ArrayList<>(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java index 535c6b9483..43dbc37f4f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Obeo. and others + * Copyright (C) 2015, 2022 Obeo and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -38,6 +38,8 @@ public class PrePushHook extends GitHook<String> { private String refs; + private boolean dryRun; + /** * Constructor for PrePushHook * <p> @@ -145,6 +147,27 @@ public class PrePushHook extends GitHook<String> { } /** + * Sets whether the push is a dry run. + * + * @param dryRun + * {@code true} if the push is a dry run, {@code false} otherwise + * @since 6.2 + */ + public void setDryRun(boolean dryRun) { + this.dryRun = dryRun; + } + + /** + * Tells whether the push is a dry run. + * + * @return {@code true} if the push is a dry run, {@code false} otherwise + * @since 6.2 + */ + protected boolean isDryRun() { + return dryRun; + } + + /** * Set Refs * * @param toRefs 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 16b3f372ef..17e359de49 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -265,6 +265,9 @@ public class JGitText extends TranslationBundle { /***/ public String deletingNotSupported; /***/ public String destinationIsNotAWildcard; /***/ public String detachedHeadDetected; + /***/ public String diffToolNotGivenError; + /***/ public String diffToolNotSpecifiedInGitAttributesError; + /***/ public String diffToolNullError; /***/ public String dirCacheDoesNotHaveABackingFile; /***/ public String dirCacheFileIsNotLocked; /***/ public String dirCacheIsNotLocked; @@ -421,6 +424,7 @@ public class JGitText extends TranslationBundle { /***/ public String invalidModeFor; /***/ public String invalidModeForPath; /***/ public String invalidNameContainsDotDot; + /***/ public String invalidNegativeAndForce; /***/ public String invalidObject; /***/ public String invalidOldIdSent; /***/ public String invalidPacketLineHeader; @@ -484,6 +488,8 @@ public class JGitText extends TranslationBundle { /***/ public String mergeUsingStrategyResultedInDescription; /***/ public String mergeRecursiveConflictsWhenMergingCommonAncestors; /***/ public String mergeRecursiveTooManyMergeBasesFor; + /***/ public String mergeToolNotGivenError; + /***/ public String mergeToolNullError; /***/ public String messageAndTaggerNotAllowedInUnannotatedTags; /***/ public String minutesAgo; /***/ public String mismatchOffset; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandExecutor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandExecutor.java new file mode 100644 index 0000000000..668adeab65 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandExecutor.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2018-2021, Andre Bossert <andre.bossert@siemens.com> + * + * 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.diffmergetool; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Map; + +import org.eclipse.jgit.errors.NoWorkTreeException; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.FS.ExecutionResult; +import org.eclipse.jgit.util.FS_POSIX; +import org.eclipse.jgit.util.FS_Win32; +import org.eclipse.jgit.util.FS_Win32_Cygwin; +import org.eclipse.jgit.util.StringUtils; + +/** + * Runs a command with help of FS. + */ +public class CommandExecutor { + + private FS fs; + + private boolean checkExitCode; + + private File commandFile; + + private boolean useMsys2; + + /** + * @param fs + * the file system + * @param checkExitCode + * should the exit code be checked for errors ? + */ + public CommandExecutor(FS fs, boolean checkExitCode) { + this.fs = fs; + this.checkExitCode = checkExitCode; + } + + /** + * @param command + * the command string + * @param workingDir + * the working directory + * @param env + * the environment + * @return the execution result + * @throws ToolException + * @throws InterruptedException + * @throws IOException + */ + public ExecutionResult run(String command, File workingDir, + Map<String, String> env) + throws ToolException, IOException, InterruptedException { + String[] commandArray = createCommandArray(command); + try { + ProcessBuilder pb = fs.runInShell(commandArray[0], + Arrays.copyOfRange(commandArray, 1, commandArray.length)); + pb.directory(workingDir); + Map<String, String> envp = pb.environment(); + if (env != null) { + envp.putAll(env); + } + ExecutionResult result = fs.execute(pb, null); + int rc = result.getRc(); + if (rc != 0) { + boolean execError = isCommandExecutionError(rc); + if (checkExitCode || execError) { + throw new ToolException( + "JGit: tool execution return code: " + rc + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + "checkExitCode: " + checkExitCode + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + "execError: " + execError + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + + "stderr: \n" //$NON-NLS-1$ + + new String( + result.getStderr().toByteArray()), + result, execError); + } + } + return result; + } finally { + deleteCommandArray(); + } + } + + /** + * @param path + * the executable path + * @param workingDir + * the working directory + * @param env + * the environment + * @return the execution result + * @throws ToolException + * @throws InterruptedException + * @throws IOException + */ + public boolean checkExecutable(String path, File workingDir, + Map<String, String> env) + throws ToolException, IOException, InterruptedException { + checkUseMsys2(path); + String command = null; + if (fs instanceof FS_Win32 && !useMsys2) { + Path p = Paths.get(path); + // Win32 (and not cygwin or MSYS2) where accepts only command / exe + // name as parameter + // so check if exists and executable in this case + if (p.isAbsolute() && Files.isExecutable(p)) { + return true; + } + // try where command for all other cases + command = "where " + ExternalToolUtils.quotePath(path); //$NON-NLS-1$ + } else { + command = "which " + ExternalToolUtils.quotePath(path); //$NON-NLS-1$ + } + boolean available = true; + try { + ExecutionResult rc = run(command, workingDir, env); + if (rc.getRc() != 0) { + available = false; + } + } catch (IOException | InterruptedException | NoWorkTreeException + | ToolException e) { + // no op: is true to not hide possible tools from user + } + return available; + } + + private void deleteCommandArray() { + deleteCommandFile(); + } + + private String[] createCommandArray(String command) + throws ToolException, IOException { + String[] commandArray = null; + checkUseMsys2(command); + createCommandFile(command); + if (fs instanceof FS_POSIX) { + commandArray = new String[1]; + commandArray[0] = commandFile.getCanonicalPath(); + } else if (fs instanceof FS_Win32) { + if (useMsys2) { + commandArray = new String[3]; + commandArray[0] = "bash.exe"; //$NON-NLS-1$ + commandArray[1] = "-c"; //$NON-NLS-1$ + commandArray[2] = commandFile.getCanonicalPath().replace("\\", //$NON-NLS-1$ + "/"); //$NON-NLS-1$ + } else { + commandArray = new String[1]; + commandArray[0] = commandFile.getCanonicalPath(); + } + } else if (fs instanceof FS_Win32_Cygwin) { + commandArray = new String[1]; + commandArray[0] = commandFile.getCanonicalPath().replace("\\", "/"); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + throw new ToolException( + "JGit: file system not supported: " + fs.toString()); //$NON-NLS-1$ + } + return commandArray; + } + + private void checkUseMsys2(String command) { + useMsys2 = false; + String useMsys2Str = System.getProperty("jgit.usemsys2bash"); //$NON-NLS-1$ + if (!StringUtils.isEmptyOrNull(useMsys2Str)) { + if (useMsys2Str.equalsIgnoreCase("auto")) { //$NON-NLS-1$ + useMsys2 = command.contains(".sh"); //$NON-NLS-1$ + } else { + useMsys2 = Boolean.parseBoolean(useMsys2Str); + } + } + } + + private void createCommandFile(String command) + throws ToolException, IOException { + String fileExtension = null; + if (useMsys2 || fs instanceof FS_POSIX + || fs instanceof FS_Win32_Cygwin) { + fileExtension = ".sh"; //$NON-NLS-1$ + } else if (fs instanceof FS_Win32) { + fileExtension = ".cmd"; //$NON-NLS-1$ + command = "@echo off" + System.lineSeparator() + command //$NON-NLS-1$ + + System.lineSeparator() + "exit /B %ERRORLEVEL%"; //$NON-NLS-1$ + } else { + throw new ToolException( + "JGit: file system not supported: " + fs.toString()); //$NON-NLS-1$ + } + commandFile = File.createTempFile(".__", //$NON-NLS-1$ + "__jgit_tool" + fileExtension); //$NON-NLS-1$ + try (OutputStream outStream = new FileOutputStream(commandFile)) { + byte[] strToBytes = command.getBytes(); + outStream.write(strToBytes); + outStream.close(); + } + commandFile.setExecutable(true); + } + + private void deleteCommandFile() { + if (commandFile != null && commandFile.exists()) { + commandFile.delete(); + } + } + + private boolean isCommandExecutionError(int rc) { + if (useMsys2 || fs instanceof FS_POSIX + || fs instanceof FS_Win32_Cygwin) { + // 126: permission for executing command denied + // 127: command not found + if ((rc == 126) || (rc == 127)) { + return true; + } + } + else if (fs instanceof FS_Win32) { + // 9009, 0x2331: Program is not recognized as an internal or + // external command, operable program or batch file. Indicates that + // command, application name or path has been misspelled when + // configuring the Action. + if (rc == 9009) { + return true; + } + } + return false; + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandLineDiffTool.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandLineDiffTool.java index 509515c37a..00dec32718 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandLineDiffTool.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandLineDiffTool.java @@ -111,7 +111,7 @@ public enum CommandLineDiffTool { * See: <a href= * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> */ - gvimdiff("gviewdiff", "\"$LOCAL\" \"$REMOTE\""), + gvimdiff("gvimdiff", "\"$LOCAL\" \"$REMOTE\""), /** * See: <a href= * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> @@ -160,7 +160,7 @@ public enum CommandLineDiffTool { * See: <a href= * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> */ - vimdiff("viewdiff", gvimdiff), + vimdiff("vimdiff", gvimdiff), /** * See: <a href= * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandLineMergeTool.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandLineMergeTool.java new file mode 100644 index 0000000000..3a22124328 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandLineMergeTool.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com> + * + * 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.diffmergetool; + +/** + * Pre-defined merge tools. + * + * Adds same merge tools as also pre-defined in C-Git see "git-core\mergetools\" + * see links to command line parameter description for the tools + * + * <pre> + * araxis + * bc + * bc3 + * codecompare + * deltawalker + * diffmerge + * diffuse + * ecmerge + * emerge + * examdiff + * guiffy + * gvimdiff + * gvimdiff2 + * gvimdiff3 + * kdiff3 + * kompare + * meld + * opendiff + * p4merge + * tkdiff + * tortoisemerge + * vimdiff + * vimdiff2 + * vimdiff3 + * winmerge + * xxdiff + * </pre> + * + */ +@SuppressWarnings("nls") +public enum CommandLineMergeTool { + /** + * See: <a href= + * "https://www.araxis.com/merge/documentation-windows/command-line.en">https://www.araxis.com/merge/documentation-windows/command-line.en</a> + */ + araxis("compare", + "-wait -merge -3 -a1 \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"", + "-wait -2 \"$LOCAL\" \"$REMOTE\" \"$MERGED\"", + false), + /** + * See: <a href= + * "https://www.scootersoftware.com/v4help/index.html?command_line_reference.html">https://www.scootersoftware.com/v4help/index.html?command_line_reference.html</a> + */ + bc("bcomp", "\"$LOCAL\" \"$REMOTE\" \"$BASE\" --mergeoutput=\"$MERGED\"", + "\"$LOCAL\" \"$REMOTE\" --mergeoutput=\"$MERGED\"", + false), + /** + * See: <a href= + * "https://www.scootersoftware.com/v4help/index.html?command_line_reference.html">https://www.scootersoftware.com/v4help/index.html?command_line_reference.html</a> + */ + bc3("bcompare", bc), + /** + * See: <a href= + * "https://www.devart.com/codecompare/docs/index.html?merging_via_command_line.htm">https://www.devart.com/codecompare/docs/index.html?merging_via_command_line.htm</a> + */ + codecompare("CodeMerge", + "-MF=\"$LOCAL\" -TF=\"$REMOTE\" -BF=\"$BASE\" -RF=\"$MERGED\"", + "-MF=\"$LOCAL\" -TF=\"$REMOTE\" -RF=\"$MERGED\"", + false), + /** + * See: <a href= + * "https://www.deltawalker.com/integrate/command-line">https://www.deltawalker.com/integrate/command-line</a> + * <p> + * Hint: $(pwd) command must be defined + * </p> + */ + deltawalker("DeltaWalker", + "\"$LOCAL\" \"$REMOTE\" \"$BASE\" -pwd=\"$(pwd)\" -merged=\"$MERGED\"", + "\"$LOCAL\" \"$REMOTE\" -pwd=\"$(pwd)\" -merged=\"$MERGED\"", + true), + /** + * See: <a href= + * "https://sourcegear.com/diffmerge/webhelp/sec__clargs__diff.html">https://sourcegear.com/diffmerge/webhelp/sec__clargs__diff.html</a> + */ + diffmerge("diffmerge", //$NON-NLS-1$ + "--merge --result=\"$MERGED\" \"$LOCAL\" \"$BASE\" \"$REMOTE\"", + "--merge --result=\"$MERGED\" \"$LOCAL\" \"$REMOTE\"", + true), + /** + * See: <a href= + * "http://diffuse.sourceforge.net/manual.html#introduction-usage">http://diffuse.sourceforge.net/manual.html#introduction-usage</a> + * <p> + * Hint: check the ' | cat' for the call + * </p> + */ + diffuse("diffuse", "\"$LOCAL\" \"$MERGED\" \"$REMOTE\" \"$BASE\"", + "\"$LOCAL\" \"$MERGED\" \"$REMOTE\"", false), + /** + * See: <a href= + * "http://www.elliecomputing.com/en/OnlineDoc/ecmerge_en/44205167.asp">http://www.elliecomputing.com/en/OnlineDoc/ecmerge_en/44205167.asp</a> + */ + ecmerge("ecmerge", + "--default --mode=merge3 \"$BASE\" \"$LOCAL\" \"$REMOTE\" --to=\"$MERGED\"", + "--default --mode=merge2 \"$LOCAL\" \"$REMOTE\" --to=\"$MERGED\"", + false), + /** + * See: <a href= + * "https://www.gnu.org/software/emacs/manual/html_node/emacs/Overview-of-Emerge.html">https://www.gnu.org/software/emacs/manual/html_node/emacs/Overview-of-Emerge.html</a> + * <p> + * Hint: $(basename) command must be defined + * </p> + */ + emerge("emacs", + "-f emerge-files-with-ancestor-command \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$(basename \"$MERGED\")\"", + "-f emerge-files-command \"$LOCAL\" \"$REMOTE\" \"$(basename \"$MERGED\")\"", + true), + /** + * See: <a href= + * "https://www.prestosoft.com/ps.asp?page=htmlhelp/edp/command_line_options">https://www.prestosoft.com/ps.asp?page=htmlhelp/edp/command_line_options</a> + */ + examdiff("ExamDiff", + "-merge \"$LOCAL\" \"$BASE\" \"$REMOTE\" -o:\"$MERGED\" -nh", + "-merge \"$LOCAL\" \"$REMOTE\" -o:\"$MERGED\" -nh", + false), + /** + * See: <a href= + * "https://www.guiffy.com/help/GuiffyHelp/GuiffyCmd.html">https://www.guiffy.com/help/GuiffyHelp/GuiffyCmd.html</a> + */ + guiffy("guiffy", "-s \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$MERGED\"", + "-m \"$LOCAL\" \"$REMOTE\" \"$MERGED\"", true), + /** + * See: <a href= + * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> + */ + gvimdiff("gvim", + "-f -d -c '4wincmd w | wincmd J' \"$LOCAL\" \"$BASE\" \"$REMOTE\" \"$MERGED\"", + "-f -d -c 'wincmd l' \"$LOCAL\" \"$MERGED\" \"$REMOTE\"", + true), + /** + * See: <a href= + * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> + */ + gvimdiff2("gvim", "-f -d -c 'wincmd l' \"$LOCAL\" \"$MERGED\" \"$REMOTE\"", + "-f -d -c 'wincmd l' \"$LOCAL\" \"$MERGED\" \"$REMOTE\"", true), + /** + * See: <a href= "http://vimdoc.sourceforge.net/htmldoc/diff.html"></a> + */ + gvimdiff3("gvim", + "-f -d -c 'hid | hid | hid' \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$MERGED\"", + "-f -d -c 'hid | hid' \"$LOCAL\" \"$REMOTE\" \"$MERGED\"", true), + /** + * See: <a href= + * "http://kdiff3.sourceforge.net/doc/documentation.html">http://kdiff3.sourceforge.net/doc/documentation.html</a> + */ + kdiff3("kdiff3", + "--auto --L1 \"$MERGED (Base)\" --L2 \"$MERGED (Local)\" --L3 \"$MERGED (Remote)\" -o \"$MERGED\" \"$BASE\" \"$LOCAL\" \"$REMOTE\"", + "--auto --L1 \"$MERGED (Local)\" --L2 \"$MERGED (Remote)\" -o \"$MERGED\" \"$LOCAL\" \"$REMOTE\"", + true), + /** + * See: <a href= + * "http://meldmerge.org/help/file-mode.html">http://meldmerge.org/help/file-mode.html</a> + * <p> + * Hint: use meld with output option only (new versions) + * </p> + */ + meld("meld", "--output=\"$MERGED\" \"$LOCAL\" \"$BASE\" \"$REMOTE\"", + "\"$LOCAL\" \"$MERGED\" \"$REMOTE\"", + false), + /** + * See: <a href= + * "http://www.manpagez.com/man/1/opendiff/">http://www.manpagez.com/man/1/opendiff/</a> + * <p> + * Hint: check the ' | cat' for the call + * </p> + */ + opendiff("opendiff", + "\"$LOCAL\" \"$REMOTE\" -ancestor \"$BASE\" -merge \"$MERGED\"", + "\"$LOCAL\" \"$REMOTE\" -merge \"$MERGED\"", + false), + /** + * See: <a href= + * "https://www.perforce.com/manuals/v15.1/cmdref/p4_merge.html">https://www.perforce.com/manuals/v15.1/cmdref/p4_merge.html</a> + * <p> + * Hint: check how to fix "no base present" / create_virtual_base problem + * </p> + */ + p4merge("p4merge", "\"$BASE\" \"$REMOTE\" \"$LOCAL\" \"$MERGED\"", + "\"$REMOTE\" \"$LOCAL\" \"$MERGED\"", false), + /** + * See: <a href= + * "http://linux.math.tifr.res.in/manuals/man/tkdiff.html">http://linux.math.tifr.res.in/manuals/man/tkdiff.html</a> + */ + tkdiff("tkdiff", "-a \"$BASE\" -o \"$MERGED\" \"$LOCAL\" \"$REMOTE\"", + "-o \"$MERGED\" \"$LOCAL\" \"$REMOTE\"", + true), + /** + * See: <a href= + * "https://tortoisegit.org/docs/tortoisegitmerge/tme-automation.html#tme-automation-basics">https://tortoisegit.org/docs/tortoisegitmerge/tme-automation.html#tme-automation-basics</a> + * <p> + * Hint: merge without base is not supported + * </p> + * <p> + * Hint: cannot diff + * </p> + */ + tortoisegitmerge("tortoisegitmerge", + "-base \"$BASE\" -mine \"$LOCAL\" -theirs \"$REMOTE\" -merged \"$MERGED\"", + null, false), + /** + * See: <a href= + * "https://tortoisegit.org/docs/tortoisegitmerge/tme-automation.html#tme-automation-basics">https://tortoisegit.org/docs/tortoisegitmerge/tme-automation.html#tme-automation-basics</a> + * <p> + * Hint: merge without base is not supported + * </p> + * <p> + * Hint: cannot diff + * </p> + */ + tortoisemerge("tortoisemerge", + "-base:\"$BASE\" -mine:\"$LOCAL\" -theirs:\"$REMOTE\" -merged:\"$MERGED\"", + null, false), + /** + * See: <a href= + * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> + */ + vimdiff("vim", gvimdiff), + /** + * See: <a href= + * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> + */ + vimdiff2("vim", gvimdiff2), + /** + * See: <a href= + * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> + */ + vimdiff3("vim", gvimdiff3), + /** + * See: <a href= + * "http://manual.winmerge.org/Command_line.html">http://manual.winmerge.org/Command_line.html</a> + * <p> + * Hint: check how 'mergetool_find_win32_cmd "WinMergeU.exe" "WinMerge"' + * works + * </p> + */ + winmerge("WinMergeU", + "-u -e -dl Local -dr Remote \"$LOCAL\" \"$REMOTE\" \"$MERGED\"", + "-u -e -dl Local -dr Remote \"$LOCAL\" \"$REMOTE\" \"$MERGED\"", + false), + /** + * See: <a href= + * "http://furius.ca/xxdiff/doc/xxdiff-doc.html">http://furius.ca/xxdiff/doc/xxdiff-doc.html</a> + */ + xxdiff("xxdiff", + "-X --show-merged-pane -R 'Accel.SaveAsMerged: \"Ctrl+S\"' -R 'Accel.Search: \"Ctrl+F\"' -R 'Accel.SearchForward: \"Ctrl+G\"' --merged-file \"$MERGED\" \"$LOCAL\" \"$BASE\" \"$REMOTE\"", + "-X -R 'Accel.SaveAsMerged: \"Ctrl+S\"' -R 'Accel.Search: \"Ctrl+F\"' -R 'Accel.SearchForward: \"Ctrl+G\"' --merged-file \"$MERGED\" \"$LOCAL\" \"$REMOTE\"", + false); + + CommandLineMergeTool(String path, String parametersWithBase, + String parametersWithoutBase, + boolean exitCodeTrustable) { + this.path = path; + this.parametersWithBase = parametersWithBase; + this.parametersWithoutBase = parametersWithoutBase; + this.exitCodeTrustable = exitCodeTrustable; + } + + CommandLineMergeTool(CommandLineMergeTool from) { + this(from.getPath(), from.getParameters(true), + from.getParameters(false), from.isExitCodeTrustable()); + } + + CommandLineMergeTool(String path, CommandLineMergeTool from) { + this(path, from.getParameters(true), from.getParameters(false), + from.isExitCodeTrustable()); + } + + private final String path; + + private final String parametersWithBase; + + private final String parametersWithoutBase; + + private final boolean exitCodeTrustable; + + /** + * @return path + */ + public String getPath() { + return path; + } + + /** + * @param withBase + * return parameters with base present? + * @return parameters with or without base present + */ + public String getParameters(boolean withBase) { + if (withBase) { + return parametersWithBase; + } + return parametersWithoutBase; + } + + /** + * @return parameters + */ + public boolean isExitCodeTrustable() { + return exitCodeTrustable; + } + + /** + * @return true if command with base present is valid, false otherwise + */ + public boolean canMergeWithoutBasePresent() { + return parametersWithoutBase != null; + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffToolConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffToolConfig.java index 551f634f2d..c8b04f90f2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffToolConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffToolConfig.java @@ -49,9 +49,10 @@ public class DiffToolConfig { toolName = rc.getString(CONFIG_DIFF_SECTION, null, CONFIG_KEY_TOOL); guiToolName = rc.getString(CONFIG_DIFF_SECTION, null, CONFIG_KEY_GUITOOL); - prompt = rc.getBoolean(CONFIG_DIFFTOOL_SECTION, CONFIG_KEY_PROMPT, + prompt = rc.getBoolean(CONFIG_DIFFTOOL_SECTION, toolName, + CONFIG_KEY_PROMPT, true); - String trustStr = rc.getString(CONFIG_DIFFTOOL_SECTION, null, + String trustStr = rc.getString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_TRUST_EXIT_CODE); if (trustStr != null) { trustExitCode = Boolean.parseBoolean(trustStr) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java index 39729a4eec..7cedd82995 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java @@ -1,5 +1,6 @@ /* - * Copyright (C) 2018-2021, Andre Bossert <andre.bossert@siemens.com> + * Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com> + * Copyright (C) 2019, Tim Neumann <tim.neumann@advantest.com> * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -10,24 +11,43 @@ package org.eclipse.jgit.internal.diffmergetool; -import java.util.TreeMap; +import java.io.File; +import java.io.IOException; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; import java.util.Set; +import java.util.TreeMap; +import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.lib.internal.BooleanTriState; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.FS.ExecutionResult; +import org.eclipse.jgit.util.StringUtils; /** * Manages diff tools. */ public class DiffTools { + private final FS fs; + + private final File gitDir; + + private final File workTree; + private final DiffToolConfig config; - private Map<String, ExternalDiffTool> predefinedTools; + private final Repository repo; - private Map<String, ExternalDiffTool> userDefinedTools; + private final Map<String, ExternalDiffTool> predefinedTools; + + private final Map<String, ExternalDiffTool> userDefinedTools; /** * Creates the external diff-tools manager for given repository. @@ -36,46 +56,220 @@ public class DiffTools { * the repository */ public DiffTools(Repository repo) { - config = repo.getConfig().get(DiffToolConfig.KEY); - setupPredefinedTools(); - setupUserDefinedTools(); + this(repo, repo.getConfig()); + } + + /** + * Creates the external merge-tools manager for given configuration. + * + * @param config + * the git configuration + */ + public DiffTools(StoredConfig config) { + this(null, config); + } + + private DiffTools(Repository repo, StoredConfig config) { + this.repo = repo; + this.config = config.get(DiffToolConfig.KEY); + this.gitDir = repo == null ? null : repo.getDirectory(); + this.fs = repo == null ? FS.DETECTED : repo.getFS(); + this.workTree = repo == null ? null : repo.getWorkTree(); + predefinedTools = setupPredefinedTools(); + userDefinedTools = setupUserDefinedTools(predefinedTools); } /** * Compare two versions of a file. * - * @param newPath - * the new file path - * @param oldPath - * the old file path - * @param newId - * the new object ID - * @param oldId - * the old object ID + * @param localFile + * The local/left version of the file. + * @param remoteFile + * The remote/right version of the file. * @param toolName - * the selected tool name (can be null) + * Optionally the name of the tool to use. If not given the + * default tool will be used. * @param prompt - * the prompt option + * Optionally a flag whether to prompt the user before compare. + * If not given the default will be used. * @param gui - * the GUI option + * A flag whether to prefer a gui tool. + * @param trustExitCode + * Optionally a flag whether to trust the exit code of the tool. + * If not given the default will be used. + * @param promptHandler + * The handler to use when needing to prompt the user if he wants + * to continue. + * @param noToolHandler + * The handler to use when needing to inform the user, that no + * tool is configured. + * @return the optioanl result of executing the tool if it was executed + * @throws ToolException + * when the tool fails + */ + public Optional<ExecutionResult> compare(FileElement localFile, + FileElement remoteFile, Optional<String> toolName, + BooleanTriState prompt, boolean gui, BooleanTriState trustExitCode, + PromptContinueHandler promptHandler, + InformNoToolHandler noToolHandler) throws ToolException { + + String toolNameToUse; + + if (toolName == null) { + throw new ToolException(JGitText.get().diffToolNullError); + } + + if (toolName.isPresent()) { + toolNameToUse = toolName.get(); + } else { + toolNameToUse = getDefaultToolName(gui); + } + + if (StringUtils.isEmptyOrNull(toolNameToUse)) { + throw new ToolException(JGitText.get().diffToolNotGivenError); + } + + boolean doPrompt; + if (prompt != BooleanTriState.UNSET) { + doPrompt = prompt == BooleanTriState.TRUE; + } else { + doPrompt = isInteractive(); + } + + if (doPrompt) { + if (!promptHandler.prompt(toolNameToUse)) { + return Optional.empty(); + } + } + + boolean trust; + if (trustExitCode != BooleanTriState.UNSET) { + trust = trustExitCode == BooleanTriState.TRUE; + } else { + trust = config.isTrustExitCode(); + } + + ExternalDiffTool tool = getTool(toolNameToUse); + if (tool == null) { + throw new ToolException( + "External diff tool is not defined: " + toolNameToUse); //$NON-NLS-1$ + } + + return Optional.of( + compare(localFile, remoteFile, tool, trust)); + } + + /** + * Compare two versions of a file. + * + * @param localFile + * the local file element + * @param remoteFile + * the remote file element + * @param tool + * the selected tool * @param trustExitCode * the "trust exit code" option - * @return the return code from executed tool + * @return the execution result from tool + * @throws ToolException */ - public int compare(String newPath, String oldPath, String newId, - String oldId, String toolName, BooleanTriState prompt, - BooleanTriState gui, BooleanTriState trustExitCode) { - return 0; + public ExecutionResult compare(FileElement localFile, + FileElement remoteFile, ExternalDiffTool tool, + boolean trustExitCode) throws ToolException { + try { + if (tool == null) { + throw new ToolException(JGitText + .get().diffToolNotSpecifiedInGitAttributesError); + } + // prepare the command (replace the file paths) + String command = ExternalToolUtils.prepareCommand(tool.getCommand(), + localFile, remoteFile, null, null); + // prepare the environment + Map<String, String> env = ExternalToolUtils.prepareEnvironment( + gitDir, localFile, remoteFile, null, null); + // execute the tool + CommandExecutor cmdExec = new CommandExecutor(fs, trustExitCode); + return cmdExec.run(command, workTree, env); + } catch (IOException | InterruptedException e) { + throw new ToolException(e); + } finally { + localFile.cleanTemporaries(); + remoteFile.cleanTemporaries(); + } } /** - * @return the tool names + * Get user defined tool names. + * + * @return the user defined tool names */ - public Set<String> getToolNames() { - return config.getToolNames(); + public Set<String> getUserDefinedToolNames() { + return userDefinedTools.keySet(); } /** + * Get predefined tool names. + * + * @return the predefined tool names + */ + public Set<String> getPredefinedToolNames() { + return predefinedTools.keySet(); + } + + /** + * Get all tool names. + * + * @return the all tool names (default or available tool name is the first + * in the set) + */ + public Set<String> getAllToolNames() { + String defaultName = getDefaultToolName(false); + if (defaultName == null) { + defaultName = getFirstAvailableTool(); + } + return ExternalToolUtils.createSortedToolSet(defaultName, + getUserDefinedToolNames(), getPredefinedToolNames()); + } + + /** + * Provides {@link Optional} with the name of an external diff tool if + * specified in git configuration for a path. + * + * The formed git configuration results from global rules as well as merged + * rules from info and worktree attributes. + * + * Triggers {@link TreeWalk} until specified path found in the tree. + * + * @param path + * path to the node in repository to parse git attributes for + * @return name of the difftool if set + * @throws ToolException + */ + public Optional<String> getExternalToolFromAttributes(final String path) + throws ToolException { + return ExternalToolUtils.getExternalToolFromAttributes(repo, path, + ExternalToolUtils.KEY_DIFF_TOOL); + } + + /** + * Checks the availability of the predefined tools in the system. + * + * @return set of predefined available tools + */ + public Set<String> getPredefinedAvailableTools() { + Map<String, ExternalDiffTool> defTools = getPredefinedTools(true); + Set<String> availableTools = new LinkedHashSet<>(); + for (Entry<String, ExternalDiffTool> elem : defTools.entrySet()) { + if (elem.getValue().isAvailable()) { + availableTools.add(elem.getKey()); + } + } + return availableTools; + } + + /** + * Get user defined tools map. + * * @return the user defined tools */ public Map<String, ExternalDiffTool> getUserDefinedTools() { @@ -83,61 +277,106 @@ public class DiffTools { } /** - * @return the available predefined tools + * Get predefined tools map. + * + * @param checkAvailability + * true: for checking if tools can be executed; ATTENTION: this + * check took some time, do not execute often (store the map for + * other actions); false: availability is NOT checked: + * isAvailable() returns default false is this case! + * @return the predefined tools with optionally checked availability (long + * running operation) */ - public Map<String, ExternalDiffTool> getAvailableTools() { + public Map<String, ExternalDiffTool> getPredefinedTools( + boolean checkAvailability) { + if (checkAvailability) { + for (ExternalDiffTool tool : predefinedTools.values()) { + PreDefinedDiffTool predefTool = (PreDefinedDiffTool) tool; + predefTool.setAvailable(ExternalToolUtils.isToolAvailable(fs, + gitDir, workTree, predefTool.getPath())); + } + } return Collections.unmodifiableMap(predefinedTools); } /** - * @return the NOT available predefined tools + * Get first available tool name. + * + * @return the name of first available predefined tool or null */ - public Map<String, ExternalDiffTool> getNotAvailableTools() { - return Collections.unmodifiableMap(new TreeMap<>()); + public String getFirstAvailableTool() { + for (ExternalDiffTool tool : predefinedTools.values()) { + if (ExternalToolUtils.isToolAvailable(fs, gitDir, workTree, + tool.getPath())) { + return tool.getName(); + } + } + return null; } /** + * Get default (gui-)tool name. + * * @param gui * use the diff.guitool setting ? * @return the default tool name */ - public String getDefaultToolName(BooleanTriState gui) { - return gui != BooleanTriState.UNSET ? "my_gui_tool" //$NON-NLS-1$ - : "my_default_toolname"; //$NON-NLS-1$ + public String getDefaultToolName(boolean gui) { + String guiToolName; + if (gui) { + guiToolName = config.getDefaultGuiToolName(); + if (guiToolName != null) { + return guiToolName; + } + } + return config.getDefaultToolName(); } /** + * Is interactive diff (prompt enabled) ? + * * @return is interactive (config prompt enabled) ? */ public boolean isInteractive() { - return false; + return config.isPrompt(); + } + + private ExternalDiffTool getTool(final String name) { + ExternalDiffTool tool = userDefinedTools.get(name); + if (tool == null) { + tool = predefinedTools.get(name); + } + return tool; } - private void setupPredefinedTools() { - predefinedTools = new TreeMap<>(); + private static Map<String, ExternalDiffTool> setupPredefinedTools() { + Map<String, ExternalDiffTool> tools = new TreeMap<>(); for (CommandLineDiffTool tool : CommandLineDiffTool.values()) { - predefinedTools.put(tool.name(), new PreDefinedDiffTool(tool)); + tools.put(tool.name(), new PreDefinedDiffTool(tool)); } + return tools; } - private void setupUserDefinedTools() { - userDefinedTools = new TreeMap<>(); + private Map<String, ExternalDiffTool> setupUserDefinedTools( + Map<String, ExternalDiffTool> predefTools) { + Map<String, ExternalDiffTool> tools = new TreeMap<>(); Map<String, ExternalDiffTool> userTools = config.getTools(); for (String name : userTools.keySet()) { ExternalDiffTool userTool = userTools.get(name); // if difftool.<name>.cmd is defined we have user defined tool if (userTool.getCommand() != null) { - userDefinedTools.put(name, userTool); + tools.put(name, userTool); } else if (userTool.getPath() != null) { // if difftool.<name>.path is defined we just overload the path // of predefined tool - PreDefinedDiffTool predefTool = (PreDefinedDiffTool) predefinedTools + PreDefinedDiffTool predefTool = (PreDefinedDiffTool) predefTools .get(name); if (predefTool != null) { predefTool.setPath(userTool.getPath()); } } } + return tools; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalDiffTool.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalDiffTool.java index f2d7e828cb..e01b892a53 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalDiffTool.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalDiffTool.java @@ -30,4 +30,10 @@ public interface ExternalDiffTool { */ String getCommand(); + /** + * @return availability of the tool: true if tool can be executed and false + * if not + */ + boolean isAvailable(); + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalMergeTool.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalMergeTool.java new file mode 100644 index 0000000000..0c3ddf9afe --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalMergeTool.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com> + * + * 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.diffmergetool; + +import org.eclipse.jgit.lib.internal.BooleanTriState; + +/** + * The merge tool interface. + */ +public interface ExternalMergeTool extends ExternalDiffTool { + + /** + * @return the tool "trust exit code" option + */ + BooleanTriState getTrustExitCode(); + + /** + * @param withBase + * get command with base present (true) or without base present + * (false) + * @return the tool command + */ + String getCommand(boolean withBase); + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalToolUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalToolUtils.java new file mode 100644 index 0000000000..b2dd846d70 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalToolUtils.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2018-2021, Andre Bossert <andre.bossert@siemens.com> + * + * 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.diffmergetool; + +import java.util.TreeMap; +import java.io.File; +import java.io.IOException; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import org.eclipse.jgit.attributes.Attributes; +import org.eclipse.jgit.errors.RevisionSyntaxException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.WorkingTreeIterator; +import org.eclipse.jgit.treewalk.filter.NotIgnoredFilter; +import org.eclipse.jgit.util.FS; + +/** + * Utilities for diff- and merge-tools. + */ +public class ExternalToolUtils { + + /** + * Key for merge tool git configuration section + */ + public static final String KEY_MERGE_TOOL = "mergetool"; //$NON-NLS-1$ + + /** + * Key for diff tool git configuration section + */ + public static final String KEY_DIFF_TOOL = "difftool"; //$NON-NLS-1$ + + /** + * Prepare command for execution. + * + * @param command + * the input "command" string + * @param localFile + * the local file (ours) + * @param remoteFile + * the remote file (theirs) + * @param mergedFile + * the merged file (worktree) + * @param baseFile + * the base file (can be null) + * @return the prepared (with replaced variables) command string + * @throws IOException + */ + public static String prepareCommand(String command, FileElement localFile, + FileElement remoteFile, FileElement mergedFile, + FileElement baseFile) throws IOException { + if (localFile != null) { + command = localFile.replaceVariable(command); + } + if (remoteFile != null) { + command = remoteFile.replaceVariable(command); + } + if (mergedFile != null) { + command = mergedFile.replaceVariable(command); + } + if (baseFile != null) { + command = baseFile.replaceVariable(command); + } + return command; + } + + /** + * Prepare environment needed for execution. + * + * @param gitDir + * the .git directory + * @param localFile + * the local file (ours) + * @param remoteFile + * the remote file (theirs) + * @param mergedFile + * the merged file (worktree) + * @param baseFile + * the base file (can be null) + * @return the environment map with variables and values (file paths) + * @throws IOException + */ + public static Map<String, String> prepareEnvironment(File gitDir, + FileElement localFile, FileElement remoteFile, + FileElement mergedFile, FileElement baseFile) throws IOException { + Map<String, String> env = new TreeMap<>(); + if (gitDir != null) { + env.put(Constants.GIT_DIR_KEY, gitDir.getAbsolutePath()); + } + if (localFile != null) { + localFile.addToEnv(env); + } + if (remoteFile != null) { + remoteFile.addToEnv(env); + } + if (mergedFile != null) { + mergedFile.addToEnv(env); + } + if (baseFile != null) { + baseFile.addToEnv(env); + } + return env; + } + + /** + * @param path + * the path to be quoted + * @return quoted path if it contains spaces + */ + @SuppressWarnings("nls") + public static String quotePath(String path) { + // handling of spaces in path + if (path.contains(" ")) { + // add quotes before if needed + if (!path.startsWith("\"")) { + path = "\"" + path; + } + // add quotes after if needed + if (!path.endsWith("\"")) { + path = path + "\""; + } + } + return path; + } + + /** + * @param fs + * the file system abstraction + * @param gitDir + * the .git directory + * @param directory + * the working directory + * @param path + * the tool path + * @return true if tool available and false otherwise + */ + public static boolean isToolAvailable(FS fs, File gitDir, File directory, + String path) { + boolean available = true; + try { + CommandExecutor cmdExec = new CommandExecutor(fs, false); + available = cmdExec.checkExecutable(path, directory, + prepareEnvironment(gitDir, null, null, null, null)); + } catch (Exception e) { + available = false; + } + return available; + } + + /** + * @param defaultName + * the default tool name + * @param userDefinedNames + * the user defined tool names + * @param preDefinedNames + * the pre defined tool names + * @return the sorted tool names set: first element is default tool name if + * valid, then user defined tool names and then pre defined tool + * names + */ + public static Set<String> createSortedToolSet(String defaultName, + Set<String> userDefinedNames, Set<String> preDefinedNames) { + Set<String> names = new LinkedHashSet<>(); + if (defaultName != null) { + // remove defaultName from both sets + Set<String> namesPredef = new LinkedHashSet<>(); + Set<String> namesUser = new LinkedHashSet<>(); + namesUser.addAll(userDefinedNames); + namesUser.remove(defaultName); + namesPredef.addAll(preDefinedNames); + namesPredef.remove(defaultName); + // add defaultName as first in set + names.add(defaultName); + names.addAll(namesUser); + names.addAll(namesPredef); + } else { + names.addAll(userDefinedNames); + names.addAll(preDefinedNames); + } + return names; + } + + /** + * Provides {@link Optional} with the name of an external tool if specified + * in git configuration for a path. + * + * The formed git configuration results from global rules as well as merged + * rules from info and worktree attributes. + * + * Triggers {@link TreeWalk} until specified path found in the tree. + * + * @param repository + * target repository to traverse into + * @param path + * path to the node in repository to parse git attributes for + * @param toolKey + * config key name for the tool + * @return attribute value for the given tool key if set + * @throws ToolException + */ + public static Optional<String> getExternalToolFromAttributes( + final Repository repository, final String path, + final String toolKey) throws ToolException { + try { + WorkingTreeIterator treeIterator = new FileTreeIterator(repository); + try (TreeWalk walk = new TreeWalk(repository)) { + walk.addTree(treeIterator); + walk.setFilter(new NotIgnoredFilter(0)); + while (walk.next()) { + String treePath = walk.getPathString(); + if (treePath.equals(path)) { + Attributes attrs = walk.getAttributes(); + if (attrs.containsKey(toolKey)) { + return Optional.of(attrs.getValue(toolKey)); + } + } + if (walk.isSubtree()) { + walk.enterSubtree(); + } + } + // no external tool specified + return Optional.empty(); + } + + } catch (RevisionSyntaxException | IOException e) { + throw new ToolException(e); + } + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/FileElement.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/FileElement.java new file mode 100644 index 0000000000..ba8ca54c58 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/FileElement.java @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2018-2021, Andre Bossert <andre.bossert@siemens.com> + * + * 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.diffmergetool; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; + +import org.eclipse.jgit.diff.DiffEntry; + +/** + * The element used as left or right file for compare. + * + */ +public class FileElement { + + /** + * The file element type. + * + */ + public enum Type { + /** + * The local file element (ours). + */ + LOCAL, + /** + * The remote file element (theirs). + */ + REMOTE, + /** + * The merged file element (path in worktree). + */ + MERGED, + /** + * The base file element (of ours and theirs). + */ + BASE, + /** + * The backup file element (copy of merged / conflicted). + */ + BACKUP + } + + private final String path; + + private final Type type; + + private final File workDir; + + private InputStream stream; + + private File tempFile; + + /** + * Creates file element for path. + * + * @param path + * the file path + * @param type + * the element type + */ + public FileElement(String path, Type type) { + this(path, type, null); + } + + /** + * Creates file element for path. + * + * @param path + * the file path + * @param type + * the element type + * @param workDir + * the working directory of the path (can be null, then current + * working dir is used) + */ + public FileElement(String path, Type type, File workDir) { + this(path, type, workDir, null); + } + + /** + * @param path + * the file path + * @param type + * the element type + * @param workDir + * the working directory of the path (can be null, then current + * working dir is used) + * @param stream + * the object stream to load and write on demand, @see getFile(), + * to tempFile once (can be null) + */ + public FileElement(String path, Type type, File workDir, + InputStream stream) { + this.path = path; + this.type = type; + this.workDir = workDir; + this.stream = stream; + } + + /** + * @return the file path + */ + public String getPath() { + return path; + } + + /** + * @return the element type + */ + public Type getType() { + return type; + } + + /** + * Return + * <ul> + * <li>a temporary file if already created and stream is not valid</li> + * <li>OR a real file from work tree: if no temp file was created (@see + * createTempFile()) and if no stream was set</li> + * <li>OR an empty temporary file if path is "/dev/null"</li> + * <li>OR a temporary file with stream content if stream is valid (not + * null); stream is closed and invalidated (set to null) after write to temp + * file, so stream is used only once during first call!</li> + * </ul> + * + * @return the object stream + * @throws IOException + */ + public File getFile() throws IOException { + // if we have already temp file and no stream + // then just return this temp file (it was filled from outside) + if ((tempFile != null) && (stream == null)) { + return tempFile; + } + File file = new File(workDir, path); + // if we have a stream or file is missing (path is "/dev/null") + // then optionally create temporary file and fill it with stream content + if ((stream != null) || isNullPath()) { + if (tempFile == null) { + tempFile = getTempFile(file, type.name(), null); + } + if (stream != null) { + copyFromStream(tempFile, stream); + } + // invalidate the stream, because it is used once + stream = null; + return tempFile; + } + return file; + } + + /** + * Check if path id "/dev/null" + * + * @return true if path is "/dev/null" + */ + public boolean isNullPath() { + return path.equals(DiffEntry.DEV_NULL); + } + + /** + * Create temporary file in given or system temporary directory. + * + * @param directory + * the directory for the file (can be null); if null system + * temporary directory is used + * @return temporary file in directory or in the system temporary directory + * @throws IOException + */ + public File createTempFile(File directory) throws IOException { + if (tempFile == null) { + tempFile = getTempFile(new File(path), type.name(), directory); + } + return tempFile; + } + + /** + * Delete and invalidate temporary file if necessary. + */ + public void cleanTemporaries() { + if (tempFile != null && tempFile.exists()) { + tempFile.delete(); + } + tempFile = null; + } + + /** + * Replace variable in input. + * + * @param input + * the input string + * @return the replaced input string + * @throws IOException + */ + public String replaceVariable(String input) throws IOException { + return input.replace("$" + type.name(), getFile().getPath()); //$NON-NLS-1$ + } + + /** + * Add variable to environment map. + * + * @param env + * the environment where this element should be added + * @throws IOException + */ + public void addToEnv(Map<String, String> env) throws IOException { + env.put(type.name(), getFile().getPath()); + } + + private static File getTempFile(final File file, final String midName, + final File workingDir) throws IOException { + String[] fileNameAndExtension = splitBaseFileNameAndExtension(file); + // TODO: avoid long random file name (number generated by + // createTempFile) + return File.createTempFile( + fileNameAndExtension[0] + "_" + midName + "_", //$NON-NLS-1$ //$NON-NLS-2$ + fileNameAndExtension[1], workingDir); + } + + private static void copyFromStream(final File file, + final InputStream stream) + throws IOException, FileNotFoundException { + try (OutputStream outStream = new FileOutputStream(file)) { + int read = 0; + byte[] bytes = new byte[8 * 1024]; + while ((read = stream.read(bytes)) != -1) { + outStream.write(bytes, 0, read); + } + } finally { + // stream can only be consumed once --> close it and invalidate + stream.close(); + } + } + + private static String[] splitBaseFileNameAndExtension(File file) { + String[] result = new String[2]; + result[0] = file.getName(); + result[1] = ""; //$NON-NLS-1$ + int idx = result[0].lastIndexOf("."); //$NON-NLS-1$ + // if "." was found (>-1) and last-index is not first char (>0), then + // split (same behavior like cgit) + if (idx > 0) { + result[1] = result[0].substring(idx, result[0].length()); + result[0] = result[0].substring(0, idx); + } + return result; + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/InformNoToolHandler.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/InformNoToolHandler.java new file mode 100644 index 0000000000..36b290d37d --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/InformNoToolHandler.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018-2019, Tim Neumann <Tim.Neumann@advantest.com> + * + * 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.diffmergetool; + +import java.util.List; + +/** + * A handler for when the diff/merge tool manager wants to inform the user that + * no tool has been configured and one of the default tools will be used. + */ +public interface InformNoToolHandler { + /** + * Inform the user, that no tool is configured and that one of the given + * tools is used. + * + * @param toolNames + * The tools which are tried + */ + void inform(List<String> toolNames); +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeToolConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeToolConfig.java new file mode 100644 index 0000000000..9625d5f101 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeToolConfig.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com> + * + * 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.diffmergetool; + +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CMD; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_GUITOOL; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_KEEP_BACKUP; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_KEEP_TEMPORARIES; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PATH; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PROMPT; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TOOL; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_TRUST_EXIT_CODE; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_WRITE_TO_TEMP; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGETOOL_SECTION; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_MERGE_SECTION; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.Config.SectionParser; +import org.eclipse.jgit.lib.internal.BooleanTriState; + +/** + * Keeps track of merge tool related configuration options. + */ +public class MergeToolConfig { + + /** Key for {@link Config#get(SectionParser)}. */ + public static final Config.SectionParser<MergeToolConfig> KEY = MergeToolConfig::new; + + private final String toolName; + + private final String guiToolName; + + private final boolean prompt; + + private final boolean keepBackup; + + private final boolean keepTemporaries; + + private final boolean writeToTemp; + + private final Map<String, ExternalMergeTool> tools; + + private MergeToolConfig(Config rc) { + toolName = rc.getString(CONFIG_MERGE_SECTION, null, CONFIG_KEY_TOOL); + guiToolName = rc.getString(CONFIG_MERGE_SECTION, null, + CONFIG_KEY_GUITOOL); + prompt = rc.getBoolean(CONFIG_MERGETOOL_SECTION, toolName, + CONFIG_KEY_PROMPT, true); + keepBackup = rc.getBoolean(CONFIG_MERGETOOL_SECTION, + CONFIG_KEY_KEEP_BACKUP, true); + keepTemporaries = rc.getBoolean(CONFIG_MERGETOOL_SECTION, + CONFIG_KEY_KEEP_TEMPORARIES, false); + writeToTemp = rc.getBoolean(CONFIG_MERGETOOL_SECTION, + CONFIG_KEY_WRITE_TO_TEMP, false); + tools = new HashMap<>(); + Set<String> subsections = rc.getSubsections(CONFIG_MERGETOOL_SECTION); + for (String name : subsections) { + String cmd = rc.getString(CONFIG_MERGETOOL_SECTION, name, + CONFIG_KEY_CMD); + String path = rc.getString(CONFIG_MERGETOOL_SECTION, name, + CONFIG_KEY_PATH); + BooleanTriState trustExitCode = BooleanTriState.FALSE; + String trustStr = rc.getString(CONFIG_MERGETOOL_SECTION, name, + CONFIG_KEY_TRUST_EXIT_CODE); + if (trustStr != null) { + trustExitCode = Boolean.valueOf(trustStr).booleanValue() + ? BooleanTriState.TRUE + : BooleanTriState.FALSE; + } else { + trustExitCode = BooleanTriState.UNSET; + } + if ((cmd != null) || (path != null)) { + tools.put(name, new UserDefinedMergeTool(name, path, cmd, + trustExitCode)); + } + } + } + + /** + * @return the default merge tool name (merge.tool) + */ + public String getDefaultToolName() { + return toolName; + } + + /** + * @return the default GUI merge tool name (merge.guitool) + */ + public String getDefaultGuiToolName() { + return guiToolName; + } + + /** + * @return the merge tool "prompt" option (mergetool.prompt) + */ + public boolean isPrompt() { + return prompt; + } + + /** + * @return the tool "keep backup" option + */ + public boolean isKeepBackup() { + return keepBackup; + } + + /** + * @return the tool "keepTemporaries" option + */ + public boolean isKeepTemporaries() { + return keepTemporaries; + } + + /** + * @return the tool "write to temp" option + */ + public boolean isWriteToTemp() { + return writeToTemp; + } + + /** + * @return the tools map + */ + public Map<String, ExternalMergeTool> getTools() { + return tools; + } + + /** + * @return the tool names + */ + public Set<String> getToolNames() { + return tools.keySet(); + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeTools.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeTools.java new file mode 100644 index 0000000000..b903201264 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/MergeTools.java @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com> + * Copyright (C) 2019, Tim Neumann <tim.neumann@advantest.com> + * + * 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.diffmergetool; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; + +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.internal.diffmergetool.FileElement.Type; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.lib.internal.BooleanTriState; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.StringUtils; +import org.eclipse.jgit.util.FS.ExecutionResult; + +/** + * Manages merge tools. + */ +public class MergeTools { + + private final FS fs; + + private final File gitDir; + + private final File workTree; + + private final MergeToolConfig config; + + private final Repository repo; + + private final Map<String, ExternalMergeTool> predefinedTools; + + private final Map<String, ExternalMergeTool> userDefinedTools; + + /** + * Creates the external merge-tools manager for given repository. + * + * @param repo + * the repository + */ + public MergeTools(Repository repo) { + this(repo, repo.getConfig()); + } + + /** + * Creates the external diff-tools manager for given configuration. + * + * @param config + * the git configuration + */ + public MergeTools(StoredConfig config) { + this(null, config); + } + + private MergeTools(Repository repo, StoredConfig config) { + this.repo = repo; + this.config = config.get(MergeToolConfig.KEY); + this.gitDir = repo == null ? null : repo.getDirectory(); + this.fs = repo == null ? FS.DETECTED : repo.getFS(); + this.workTree = repo == null ? null : repo.getWorkTree(); + predefinedTools = setupPredefinedTools(); + userDefinedTools = setupUserDefinedTools(predefinedTools); + } + + /** + * Merge two versions of a file with optional base file. + * + * @param localFile + * The local/left version of the file. + * @param remoteFile + * The remote/right version of the file. + * @param mergedFile + * The file for the result. + * @param baseFile + * The base version of the file. May be null. + * @param tempDir + * The tmepDir used for the files. May be null. + * @param toolName + * Optionally the name of the tool to use. If not given the + * default tool will be used. + * @param prompt + * Optionally a flag whether to prompt the user before compare. + * If not given the default will be used. + * @param gui + * A flag whether to prefer a gui tool. + * @param promptHandler + * The handler to use when needing to prompt the user if he wants + * to continue. + * @param noToolHandler + * The handler to use when needing to inform the user, that no + * tool is configured. + * @return the optional result of executing the tool if it was executed + * @throws ToolException + * when the tool fails + */ + public Optional<ExecutionResult> merge(FileElement localFile, + FileElement remoteFile, FileElement mergedFile, + FileElement baseFile, File tempDir, Optional<String> toolName, + BooleanTriState prompt, boolean gui, + PromptContinueHandler promptHandler, + InformNoToolHandler noToolHandler) throws ToolException { + + String toolNameToUse; + + if (toolName == null) { + throw new ToolException(JGitText.get().diffToolNullError); + } + + if (toolName.isPresent()) { + toolNameToUse = toolName.get(); + } else { + toolNameToUse = getDefaultToolName(gui); + + if (StringUtils.isEmptyOrNull(toolNameToUse)) { + noToolHandler.inform(new ArrayList<>(predefinedTools.keySet())); + toolNameToUse = getFirstAvailableTool(); + } + } + + if (StringUtils.isEmptyOrNull(toolNameToUse)) { + throw new ToolException(JGitText.get().diffToolNotGivenError); + } + + boolean doPrompt; + if (prompt != BooleanTriState.UNSET) { + doPrompt = prompt == BooleanTriState.TRUE; + } else { + doPrompt = isInteractive(); + } + + if (doPrompt) { + if (!promptHandler.prompt(toolNameToUse)) { + return Optional.empty(); + } + } + + ExternalMergeTool tool = getTool(toolNameToUse); + if (tool == null) { + throw new ToolException( + "External merge tool is not defined: " + toolNameToUse); //$NON-NLS-1$ + } + + return Optional.of(merge(localFile, remoteFile, mergedFile, baseFile, + tempDir, tool)); + } + + /** + * Merge two versions of a file with optional base file. + * + * @param localFile + * the local file element + * @param remoteFile + * the remote file element + * @param mergedFile + * the merged file element + * @param baseFile + * the base file element (can be null) + * @param tempDir + * the temporary directory (needed for backup and auto-remove, + * can be null) + * @param tool + * the selected tool + * @return the execution result from tool + * @throws ToolException + */ + public ExecutionResult merge(FileElement localFile, FileElement remoteFile, + FileElement mergedFile, FileElement baseFile, File tempDir, + ExternalMergeTool tool) throws ToolException { + FileElement backup = null; + ExecutionResult result = null; + try { + // create additional backup file (copy worktree file) + backup = createBackupFile(mergedFile, + tempDir != null ? tempDir : workTree); + // prepare the command (replace the file paths) + String command = ExternalToolUtils.prepareCommand( + tool.getCommand(baseFile != null), localFile, remoteFile, + mergedFile, baseFile); + // prepare the environment + Map<String, String> env = ExternalToolUtils.prepareEnvironment( + gitDir, localFile, remoteFile, mergedFile, baseFile); + boolean trust = tool.getTrustExitCode() == BooleanTriState.TRUE; + // execute the tool + CommandExecutor cmdExec = new CommandExecutor(fs, trust); + result = cmdExec.run(command, workTree, env); + // keep backup as .orig file + if (backup != null) { + keepBackupFile(mergedFile.getPath(), backup); + } + return result; + } catch (IOException | InterruptedException e) { + throw new ToolException(e); + } finally { + // always delete backup file (ignore that it was may be already + // moved to keep-backup file) + if (backup != null) { + backup.cleanTemporaries(); + } + // if the tool returns an error and keepTemporaries is set to true, + // then these temporary files will be preserved + if (!((result == null) && config.isKeepTemporaries())) { + // delete the files + localFile.cleanTemporaries(); + remoteFile.cleanTemporaries(); + if (baseFile != null) { + baseFile.cleanTemporaries(); + } + // delete temporary directory if needed + if (config.isWriteToTemp() && (tempDir != null) + && tempDir.exists()) { + tempDir.delete(); + } + } + } + } + + private FileElement createBackupFile(FileElement from, File toParentDir) + throws IOException { + FileElement backup = null; + Path path = Paths.get(from.getPath()); + if (Files.exists(path)) { + backup = new FileElement(from.getPath(), Type.BACKUP); + Files.copy(path, backup.createTempFile(toParentDir).toPath(), + StandardCopyOption.REPLACE_EXISTING); + } + return backup; + } + + /** + * Create temporary directory. + * + * @return the created temporary directory if (mergetol.writeToTemp == true) + * or null if not configured or false. + * @throws IOException + */ + public File createTempDirectory() throws IOException { + return config.isWriteToTemp() + ? Files.createTempDirectory("jgit-mergetool-").toFile() //$NON-NLS-1$ + : null; + } + + /** + * Get user defined tool names. + * + * @return the user defined tool names + */ + public Set<String> getUserDefinedToolNames() { + return userDefinedTools.keySet(); + } + + /** + * @return the predefined tool names + */ + public Set<String> getPredefinedToolNames() { + return predefinedTools.keySet(); + } + + /** + * Get all tool names. + * + * @return the all tool names (default or available tool name is the first + * in the set) + */ + public Set<String> getAllToolNames() { + String defaultName = getDefaultToolName(false); + if (defaultName == null) { + defaultName = getFirstAvailableTool(); + } + return ExternalToolUtils.createSortedToolSet(defaultName, + getUserDefinedToolNames(), getPredefinedToolNames()); + } + + /** + * Provides {@link Optional} with the name of an external merge tool if + * specified in git configuration for a path. + * + * The formed git configuration results from global rules as well as merged + * rules from info and worktree attributes. + * + * Triggers {@link TreeWalk} until specified path found in the tree. + * + * @param path + * path to the node in repository to parse git attributes for + * @return name of the difftool if set + * @throws ToolException + */ + public Optional<String> getExternalToolFromAttributes(final String path) + throws ToolException { + return ExternalToolUtils.getExternalToolFromAttributes(repo, path, + ExternalToolUtils.KEY_MERGE_TOOL); + } + + /** + * Checks the availability of the predefined tools in the system. + * + * @return set of predefined available tools + */ + public Set<String> getPredefinedAvailableTools() { + Map<String, ExternalMergeTool> defTools = getPredefinedTools(true); + Set<String> availableTools = new LinkedHashSet<>(); + for (Entry<String, ExternalMergeTool> elem : defTools.entrySet()) { + if (elem.getValue().isAvailable()) { + availableTools.add(elem.getKey()); + } + } + return availableTools; + } + + /** + * @return the user defined tools + */ + public Map<String, ExternalMergeTool> getUserDefinedTools() { + return Collections.unmodifiableMap(userDefinedTools); + } + + /** + * Get predefined tools map. + * + * @param checkAvailability + * true: for checking if tools can be executed; ATTENTION: this + * check took some time, do not execute often (store the map for + * other actions); false: availability is NOT checked: + * isAvailable() returns default false is this case! + * @return the predefined tools with optionally checked availability (long + * running operation) + */ + public Map<String, ExternalMergeTool> getPredefinedTools( + boolean checkAvailability) { + if (checkAvailability) { + for (ExternalMergeTool tool : predefinedTools.values()) { + PreDefinedMergeTool predefTool = (PreDefinedMergeTool) tool; + predefTool.setAvailable(ExternalToolUtils.isToolAvailable(fs, + gitDir, workTree, predefTool.getPath())); + } + } + return Collections.unmodifiableMap(predefinedTools); + } + + /** + * Get first available tool name. + * + * @return the name of first available predefined tool or null + */ + public String getFirstAvailableTool() { + String name = null; + for (ExternalMergeTool tool : predefinedTools.values()) { + if (ExternalToolUtils.isToolAvailable(fs, gitDir, workTree, + tool.getPath())) { + name = tool.getName(); + break; + } + } + return name; + } + + /** + * Is interactive merge (prompt enabled) ? + * + * @return is interactive (config prompt enabled) ? + */ + public boolean isInteractive() { + return config.isPrompt(); + } + + /** + * Get the default (gui-)tool name. + * + * @param gui + * use the diff.guitool setting ? + * @return the default tool name + */ + public String getDefaultToolName(boolean gui) { + return gui ? config.getDefaultGuiToolName() + : config.getDefaultToolName(); + } + + private ExternalMergeTool getTool(final String name) { + ExternalMergeTool tool = userDefinedTools.get(name); + if (tool == null) { + tool = predefinedTools.get(name); + } + return tool; + } + + private void keepBackupFile(String mergedFilePath, FileElement backup) + throws IOException { + if (config.isKeepBackup()) { + Path backupPath = backup.getFile().toPath(); + Files.move(backupPath, + backupPath.resolveSibling( + Paths.get(mergedFilePath).getFileName() + ".orig"), //$NON-NLS-1$ + StandardCopyOption.REPLACE_EXISTING); + } + } + + private Map<String, ExternalMergeTool> setupPredefinedTools() { + Map<String, ExternalMergeTool> tools = new TreeMap<>(); + for (CommandLineMergeTool tool : CommandLineMergeTool.values()) { + tools.put(tool.name(), new PreDefinedMergeTool(tool)); + } + return tools; + } + + private Map<String, ExternalMergeTool> setupUserDefinedTools( + Map<String, ExternalMergeTool> predefTools) { + Map<String, ExternalMergeTool> tools = new TreeMap<>(); + Map<String, ExternalMergeTool> userTools = config.getTools(); + for (String name : userTools.keySet()) { + ExternalMergeTool userTool = userTools.get(name); + // if mergetool.<name>.cmd is defined we have user defined tool + if (userTool.getCommand() != null) { + tools.put(name, userTool); + } else if (userTool.getPath() != null) { + // if mergetool.<name>.path is defined we just overload the path + // of predefined tool + PreDefinedMergeTool predefTool = (PreDefinedMergeTool) predefTools + .get(name); + if (predefTool != null) { + predefTool.setPath(userTool.getPath()); + if (userTool.getTrustExitCode() != BooleanTriState.UNSET) { + predefTool + .setTrustExitCode(userTool.getTrustExitCode()); + } + } + } + } + return tools; + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/PreDefinedDiffTool.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/PreDefinedDiffTool.java index 1c69fb4911..e1169a2d60 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/PreDefinedDiffTool.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/PreDefinedDiffTool.java @@ -46,17 +46,6 @@ public class PreDefinedDiffTool extends UserDefinedDiffTool { */ @Override public void setPath(String path) { - // handling of spaces in path - if (path.contains(" ")) { //$NON-NLS-1$ - // add quotes before if needed - if (!path.startsWith("\"")) { //$NON-NLS-1$ - path = "\"" + path; //$NON-NLS-1$ - } - // add quotes after if needed - if (!path.endsWith("\"")) { //$NON-NLS-1$ - path = path + "\""; //$NON-NLS-1$ - } - } super.setPath(path); } @@ -67,7 +56,7 @@ public class PreDefinedDiffTool extends UserDefinedDiffTool { */ @Override public String getCommand() { - return getPath() + " " + super.getCommand(); //$NON-NLS-1$ + return ExternalToolUtils.quotePath(getPath()) + " " + super.getCommand(); //$NON-NLS-1$ } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/PreDefinedMergeTool.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/PreDefinedMergeTool.java new file mode 100644 index 0000000000..7b28d32820 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/PreDefinedMergeTool.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com> + * + * 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.diffmergetool; + +import org.eclipse.jgit.lib.internal.BooleanTriState; + +/** + * The pre-defined merge tool. + */ +public class PreDefinedMergeTool extends UserDefinedMergeTool { + + /** + * the tool parameters without base + */ + private final String parametersWithoutBase; + + /** + * Creates the pre-defined merge tool + * + * @param name + * the name + * @param path + * the path + * @param parametersWithBase + * the tool parameters that are used together with path as + * command and "base is present" ($BASE) + * @param parametersWithoutBase + * the tool parameters that are used together with path as + * command and "base is present" ($BASE) + * @param trustExitCode + * the "trust exit code" option + */ + public PreDefinedMergeTool(String name, String path, + String parametersWithBase, String parametersWithoutBase, + BooleanTriState trustExitCode) { + super(name, path, parametersWithBase, trustExitCode); + this.parametersWithoutBase = parametersWithoutBase; + } + + /** + * Creates the pre-defined merge tool + * + * @param tool + * the command line merge tool + * + */ + public PreDefinedMergeTool(CommandLineMergeTool tool) { + this(tool.name(), tool.getPath(), tool.getParameters(true), + tool.getParameters(false), + tool.isExitCodeTrustable() ? BooleanTriState.TRUE + : BooleanTriState.FALSE); + } + + /** + * @param trustExitCode + * the "trust exit code" option + */ + @Override + public void setTrustExitCode(BooleanTriState trustExitCode) { + super.setTrustExitCode(trustExitCode); + } + + /** + * @return the tool command (with base present) + */ + @Override + public String getCommand() { + return getCommand(true); + } + + /** + * @param withBase + * get command with base present (true) or without base present + * (false) + * @return the tool command + */ + @Override + public String getCommand(boolean withBase) { + return ExternalToolUtils.quotePath(getPath()) + " " //$NON-NLS-1$ + + (withBase ? super.getCommand() : parametersWithoutBase); + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/PromptContinueHandler.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/PromptContinueHandler.java new file mode 100644 index 0000000000..6ad33df2a0 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/PromptContinueHandler.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2018-2019, Tim Neumann <Tim.Neumann@advantest.com> + * + * 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.diffmergetool; + +/** + * A handler for when the diff/merge tool manager wants to prompt the user + * whether to continue + */ +public interface PromptContinueHandler { + /** + * Prompt the user whether to continue with the next file by opening a given + * tool. + * + * @param toolName + * The name of the tool to open + * @return Whether the user wants to continue + */ + boolean prompt(String toolName); +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java new file mode 100644 index 0000000000..7cc5bb50d9 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ToolException.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2018-2021, Andre Bossert <andre.bossert@siemens.com> + * + * 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.diffmergetool; + +import org.eclipse.jgit.util.FS.ExecutionResult; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Tool exception for differentiation. + * + */ +public class ToolException extends Exception { + + private final static Logger LOG = LoggerFactory + .getLogger(ToolException.class); + + private final ExecutionResult result; + + private final boolean commandExecutionError; + + /** + * the serial version UID + */ + private static final long serialVersionUID = 1L; + + /** + * + */ + public ToolException() { + this(null, null, false); + } + + /** + * @param message + * the exception message + */ + public ToolException(String message) { + this(message, null, false); + } + + /** + * @param message + * the exception message + * @param result + * the execution result + * @param commandExecutionError + * is command execution error happened ? + */ + public ToolException(String message, ExecutionResult result, + boolean commandExecutionError) { + super(message); + this.result = result; + this.commandExecutionError = commandExecutionError; + } + + /** + * @param message + * the exception message + * @param cause + * the cause for throw + */ + public ToolException(String message, Throwable cause) { + super(message, cause); + result = null; + commandExecutionError = false; + } + + /** + * @param cause + * the cause for throw + */ + public ToolException(Throwable cause) { + super(cause); + result = null; + commandExecutionError = false; + } + + /** + * @return true if result is valid, false else + */ + public boolean isResult() { + return result != null; + } + + /** + * @return the execution result + */ + public ExecutionResult getResult() { + return result; + } + + /** + * @return true if command execution error appears, false otherwise + */ + public boolean isCommandExecutionError() { + return commandExecutionError; + } + + /** + * @return the result Stderr + */ + public String getResultStderr() { + if (result == null) { + return ""; //$NON-NLS-1$ + } + try { + return new String(result.getStderr().toByteArray()); + } catch (Exception e) { + LOG.warn("Failed to retrieve standard error output", e); //$NON-NLS-1$ + } + return ""; //$NON-NLS-1$ + } + + /** + * @return the result Stdout + */ + public String getResultStdout() { + if (result == null) { + return ""; //$NON-NLS-1$ + } + try { + return new String(result.getStdout().toByteArray()); + } catch (Exception e) { + LOG.warn("Failed to retrieve standard output", e); //$NON-NLS-1$ + } + return ""; //$NON-NLS-1$ + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/UserDefinedDiffTool.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/UserDefinedDiffTool.java index 012296eb35..eb72d01cdb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/UserDefinedDiffTool.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/UserDefinedDiffTool.java @@ -15,6 +15,8 @@ package org.eclipse.jgit.internal.diffmergetool; */ public class UserDefinedDiffTool implements ExternalDiffTool { + private boolean available; + /** * the diff tool name */ @@ -99,6 +101,23 @@ public class UserDefinedDiffTool implements ExternalDiffTool { } /** + * @return availability of the tool: true if tool can be executed and false + * if not + */ + @Override + public boolean isAvailable() { + return available; + } + + /** + * @param available + * true if tool can be found and false if not + */ + public void setAvailable(boolean available) { + this.available = available; + } + + /** * Overrides the path for the given tool. Equivalent to setting * {@code difftool.<tool>.path}. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/UserDefinedMergeTool.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/UserDefinedMergeTool.java new file mode 100644 index 0000000000..1dd2f0d793 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/UserDefinedMergeTool.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.com> + * + * 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.diffmergetool; + +import org.eclipse.jgit.lib.internal.BooleanTriState; + +/** + * The user-defined merge tool. + */ +public class UserDefinedMergeTool extends UserDefinedDiffTool + implements ExternalMergeTool { + + /** + * the merge tool "trust exit code" option + */ + private BooleanTriState trustExitCode; + + /** + * Creates the merge tool + * + * @param name + * the name + * @param path + * the path + * @param cmd + * the command + * @param trustExitCode + * the "trust exit code" option + */ + public UserDefinedMergeTool(String name, String path, String cmd, + BooleanTriState trustExitCode) { + super(name, path, cmd); + this.trustExitCode = trustExitCode; + } + /** + * @return the "trust exit code" flag + */ + @Override + public BooleanTriState getTrustExitCode() { + return trustExitCode; + } + + /** + * @param trustExitCode + * the new "trust exit code" flag + */ + protected void setTrustExitCode(BooleanTriState trustExitCode) { + this.trustExitCode = trustExitCode; + } + + /** + * @param withBase + * not used, because user-defined merge tool can only define one + * cmd -> it must handle with and without base present (empty) + * @return the tool command + */ + @Override + public String getCommand(boolean withBase) { + return getCommand(); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFileSnapshot.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFileSnapshot.java index 17bd863528..a784af8c3f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFileSnapshot.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFileSnapshot.java @@ -15,6 +15,7 @@ import java.io.RandomAccessFile; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.util.Equality; class PackFileSnapshot extends FileSnapshot { @@ -61,7 +62,8 @@ class PackFileSnapshot extends FileSnapshot { } boolean isChecksumChanged(File packFile) { - return wasChecksumChanged = checksum != MISSING_CHECKSUM + return wasChecksumChanged = !Equality.isSameInstance(checksum, + MISSING_CHECKSUM) && !checksum.equals(readChecksum(packFile)); } 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 07e38147f7..4aa2edff38 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 @@ -28,7 +28,6 @@ import static org.eclipse.jgit.lib.Ref.Storage.PACKED; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.InterruptedIOException; @@ -892,38 +891,27 @@ public class RefDirectory extends RefDatabase { } private PackedRefList readPackedRefs() throws IOException { - int maxStaleRetries = 5; - int retries = 0; - while (true) { - final FileSnapshot snapshot = FileSnapshot.save(packedRefsFile); - final MessageDigest digest = Constants.newMessageDigest(); - try (BufferedReader br = new BufferedReader(new InputStreamReader( - new DigestInputStream(new FileInputStream(packedRefsFile), - digest), - UTF_8))) { - try { - return new PackedRefList(parsePackedRefs(br), snapshot, - ObjectId.fromRaw(digest.digest())); - } catch (IOException e) { - if (FileUtils.isStaleFileHandleInCausalChain(e) - && retries < maxStaleRetries) { - if (LOG.isDebugEnabled()) { - LOG.debug(MessageFormat.format( - JGitText.get().packedRefsHandleIsStale, - Integer.valueOf(retries)), e); + try { + PackedRefList result = FileUtils.readWithRetries(packedRefsFile, + f -> { + FileSnapshot snapshot = FileSnapshot.save(f); + MessageDigest digest = Constants.newMessageDigest(); + try (BufferedReader br = new BufferedReader( + new InputStreamReader( + new DigestInputStream( + new FileInputStream(f), digest), + UTF_8))) { + return new PackedRefList(parsePackedRefs(br), + snapshot, + ObjectId.fromRaw(digest.digest())); } - retries++; - continue; - } - throw e; - } - } catch (FileNotFoundException noPackedRefs) { - if (packedRefsFile.exists()) { - throw noPackedRefs; - } - // Ignore it and leave the new list empty. - return NO_PACKED_REFS; - } + }); + return result != null ? result : NO_PACKED_REFS; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(MessageFormat + .format(JGitText.get().cannotReadFile, packedRefsFile), e); } } @@ -1090,40 +1078,55 @@ public class RefDirectory extends RefDatabase { } final int limit = 4096; - final byte[] buf; - FileSnapshot otherSnapshot = FileSnapshot.save(path); - try { - buf = IO.readSome(path, limit); - } catch (FileNotFoundException noFile) { - if (path.isFile()) { - throw noFile; + + class LooseItems { + final FileSnapshot snapshot; + + final byte[] buf; + + LooseItems(FileSnapshot snapshot, byte[] buf) { + this.snapshot = snapshot; + this.buf = buf; } - return null; // doesn't exist or no file; not a reference. } - - int n = buf.length; + LooseItems loose = null; + try { + loose = FileUtils.readWithRetries(path, + f -> new LooseItems(FileSnapshot.save(f), + IO.readSome(f, limit))); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException( + MessageFormat.format(JGitText.get().cannotReadFile, path), + e); + } + if (loose == null) { + return null; + } + int n = loose.buf.length; if (n == 0) return null; // empty file; not a reference. - if (isSymRef(buf, n)) { + if (isSymRef(loose.buf, n)) { if (n == limit) return null; // possibly truncated ref // trim trailing whitespace - while (0 < n && Character.isWhitespace(buf[n - 1])) + while (0 < n && Character.isWhitespace(loose.buf[n - 1])) n--; if (n < 6) { - String content = RawParseUtils.decode(buf, 0, n); + String content = RawParseUtils.decode(loose.buf, 0, n); throw new IOException(MessageFormat.format(JGitText.get().notARef, name, content)); } - final String target = RawParseUtils.decode(buf, 5, n); + final String target = RawParseUtils.decode(loose.buf, 5, n); if (ref != null && ref.isSymbolic() && ref.getTarget().getName().equals(target)) { assert(currentSnapshot != null); - currentSnapshot.setClean(otherSnapshot); + currentSnapshot.setClean(loose.snapshot); return ref; } - return newSymbolicRef(otherSnapshot, name, target); + return newSymbolicRef(loose.snapshot, name, target); } if (n < OBJECT_ID_STRING_LENGTH) @@ -1131,23 +1134,23 @@ public class RefDirectory extends RefDatabase { final ObjectId id; try { - id = ObjectId.fromString(buf, 0); + id = ObjectId.fromString(loose.buf, 0); if (ref != null && !ref.isSymbolic() && id.equals(ref.getTarget().getObjectId())) { assert(currentSnapshot != null); - currentSnapshot.setClean(otherSnapshot); + currentSnapshot.setClean(loose.snapshot); return ref; } } catch (IllegalArgumentException notRef) { - while (0 < n && Character.isWhitespace(buf[n - 1])) + while (0 < n && Character.isWhitespace(loose.buf[n - 1])) n--; - String content = RawParseUtils.decode(buf, 0, n); + String content = RawParseUtils.decode(loose.buf, 0, n); throw new IOException(MessageFormat.format(JGitText.get().notARef, name, content), notRef); } - return new LooseUnpeeled(otherSnapshot, name, id); + return new LooseUnpeeled(loose.snapshot, name, id); } private static boolean isSymRef(byte[] buf, int n) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BaseSearch.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BaseSearch.java index 1c24aff12d..cda456c3cb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BaseSearch.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BaseSearch.java @@ -142,6 +142,7 @@ class BaseSearch { return ptr; } + @SuppressWarnings("ReferenceEquality") private void add(AnyObjectId id, int objectType, int pathHash) { ObjectToPack obj = new ObjectToPack(id, objectType); obj.setEdge(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java index 55cc02683a..6a9b45b065 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Julian Ruppel <julian.ruppel@sap.com> + * Copyright (c) 2020, 2022 Julian Ruppel <julian.ruppel@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 @@ -29,6 +29,7 @@ import org.eclipse.jgit.lib.Config.SectionParser; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.RawParseUtils; +import org.eclipse.jgit.util.StringUtils; /** * The standard "commit" configuration parameters. @@ -44,6 +45,9 @@ public class CommitConfig { private static final String CUT = " ------------------------ >8 ------------------------\n"; //$NON-NLS-1$ + private static final char[] COMMENT_CHARS = { '#', ';', '@', '!', '$', '%', + '^', '&', '|', ':' }; + /** * How to clean up commit messages when committing. * @@ -99,6 +103,10 @@ public class CommitConfig { private CleanupMode cleanupMode; + private char commentCharacter = '#'; + + private boolean autoCommentChar = false; + private CommitConfig(Config rc) { commitTemplatePath = rc.getString(ConfigConstants.CONFIG_COMMIT_SECTION, null, ConfigConstants.CONFIG_KEY_COMMIT_TEMPLATE); @@ -106,6 +114,18 @@ public class CommitConfig { null, ConfigConstants.CONFIG_KEY_COMMIT_ENCODING); cleanupMode = rc.getEnum(ConfigConstants.CONFIG_COMMIT_SECTION, null, ConfigConstants.CONFIG_KEY_CLEANUP, CleanupMode.DEFAULT); + String comment = rc.getString(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_COMMENT_CHAR); + if (!StringUtils.isEmptyOrNull(comment)) { + if ("auto".equalsIgnoreCase(comment)) { //$NON-NLS-1$ + autoCommentChar = true; + } else { + char first = comment.charAt(0); + if (first > ' ' && first < 127) { + commentCharacter = first; + } + } + } } /** @@ -131,6 +151,51 @@ public class CommitConfig { } /** + * Retrieves the comment character set by git config + * {@code core.commentChar}. + * + * @return the character to use for comments in commit messages + * @since 6.2 + */ + public char getCommentChar() { + return commentCharacter; + } + + /** + * Determines the comment character to use for a particular text. If + * {@code core.commentChar} is "auto", tries to determine an unused + * character; if none is found, falls back to '#'. Otherwise returns the + * character given by {@code core.commentChar}. + * + * @param text + * existing text + * + * @return the character to use + * @since 6.2 + */ + public char getCommentChar(String text) { + if (isAutoCommentChar()) { + char toUse = determineCommentChar(text); + if (toUse > 0) { + return toUse; + } + return '#'; + } + return getCommentChar(); + } + + /** + * Tells whether the comment character should be determined by choosing a + * character not occurring in a commit message. + * + * @return {@code true} if git config {@code core.commentChar} is "auto" + * @since 6.2 + */ + public boolean isAutoCommentChar() { + return autoCommentChar; + } + + /** * Retrieves the {@link CleanupMode} as given by git config * {@code commit.cleanup}. * @@ -315,4 +380,41 @@ public class CommitConfig { } return false; } + + /** + * Determines a comment character by choosing one from a limited set of + * 7-bit ASCII characters that do not occur in the given text at the + * beginning of any line. If none can be determined, {@code (char) 0} is + * returned. + * + * @param text + * to get a comment character for + * @return the comment character, or {@code (char) 0} if none could be + * determined + * @since 6.2 + */ + public static char determineCommentChar(String text) { + if (StringUtils.isEmptyOrNull(text)) { + return '#'; + } + final boolean[] inUse = new boolean[127]; + for (String line : text.split("\n")) { //$NON-NLS-1$ + int len = line.length(); + for (int i = 0; i < len; i++) { + char ch = line.charAt(i); + if (!Character.isWhitespace(ch)) { + if (ch >= 0 && ch < inUse.length) { + inUse[ch] = true; + } + break; + } + } + } + for (char candidate : COMMENT_CHARS) { + if (!inUse[candidate]) { + return candidate; + } + } + return (char) 0; + } }
\ No newline at end of file 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 80d720ae41..2342cad0d7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java @@ -2,7 +2,7 @@ * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com> * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> * Copyright (C) 2012-2013, Robin Rosenberg - * Copyright (C) 2018-2021, Andre Bossert <andre.bossert@siemens.com> and others + * Copyright (C) 2018-2022, Andre Bossert <andre.bossert@siemens.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 @@ -10,6 +10,7 @@ * * SPDX-License-Identifier: BSD-3-Clause */ + package org.eclipse.jgit.lib; /** @@ -31,14 +32,14 @@ public final class ConfigConstants { public static final String CONFIG_DIFF_SECTION = "diff"; /** - * The "tool" key within "diff" section + * The "tool" key within "diff" or "merge" section * * @since 6.1 */ public static final String CONFIG_KEY_TOOL = "tool"; /** - * The "guitool" key within "diff" section + * The "guitool" key within "diff" or "merge" section * * @since 6.1 */ @@ -52,21 +53,21 @@ public final class ConfigConstants { public static final String CONFIG_DIFFTOOL_SECTION = "difftool"; /** - * The "prompt" key within "difftool" section + * The "prompt" key within "difftool" or "mergetool" section * * @since 6.1 */ public static final String CONFIG_KEY_PROMPT = "prompt"; /** - * The "trustExitCode" key within "difftool" section + * The "trustExitCode" key within "difftool" or "mergetool.<name>." section * * @since 6.1 */ public static final String CONFIG_KEY_TRUST_EXIT_CODE = "trustExitCode"; /** - * The "cmd" key within "difftool.*." section + * The "cmd" key within "difftool.*." or "mergetool.*." section * * @since 6.1 */ @@ -124,6 +125,34 @@ public final class ConfigConstants { public static final String CONFIG_MERGE_SECTION = "merge"; /** + * The "mergetool" section + * + * @since 6.2 + */ + public static final String CONFIG_MERGETOOL_SECTION = "mergetool"; + + /** + * The "keepBackup" key within "mergetool" section + * + * @since 6.2 + */ + public static final String CONFIG_KEY_KEEP_BACKUP = "keepBackup"; + + /** + * The "keepTemporaries" key within "mergetool" section + * + * @since 6.2 + */ + public static final String CONFIG_KEY_KEEP_TEMPORARIES = "keepTemporaries"; + + /** + * The "writeToTemp" key within "mergetool" section + * + * @since 6.2 + */ + public static final String CONFIG_KEY_WRITE_TO_TEMP = "writeToTemp"; + + /** * The "filter" section * @since 4.6 */ @@ -203,6 +232,13 @@ public final class ConfigConstants { public static final String CONFIG_KEY_FORCE_SIGN_ANNOTATED = "forceSignAnnotated"; /** + * The "commentChar" key. + * + * @since 6.2 + */ + public static final String CONFIG_KEY_COMMENT_CHAR = "commentChar"; + + /** * The "hooksPath" key. * * @since 5.6 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java index 4b1dbedeb1..59775c475b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2021, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -26,20 +26,41 @@ public abstract class GpgSignatureVerifierFactory { private static final Logger LOG = LoggerFactory .getLogger(GpgSignatureVerifierFactory.class); - private static volatile GpgSignatureVerifierFactory defaultFactory = loadDefault(); + private static class DefaultFactory { - private static GpgSignatureVerifierFactory loadDefault() { - try { - ServiceLoader<GpgSignatureVerifierFactory> loader = ServiceLoader - .load(GpgSignatureVerifierFactory.class); - Iterator<GpgSignatureVerifierFactory> iter = loader.iterator(); - if (iter.hasNext()) { - return iter.next(); + private static volatile GpgSignatureVerifierFactory defaultFactory = loadDefault(); + + private static GpgSignatureVerifierFactory loadDefault() { + try { + ServiceLoader<GpgSignatureVerifierFactory> loader = ServiceLoader + .load(GpgSignatureVerifierFactory.class); + Iterator<GpgSignatureVerifierFactory> iter = loader.iterator(); + if (iter.hasNext()) { + return iter.next(); + } + } catch (ServiceConfigurationError e) { + LOG.error(e.getMessage(), e); } - } catch (ServiceConfigurationError e) { - LOG.error(e.getMessage(), e); + return null; + } + + private DefaultFactory() { + // No instantiation + } + + public static GpgSignatureVerifierFactory getDefault() { + return defaultFactory; + } + + /** + * Sets the default factory. + * + * @param factory + * the new default factory + */ + public static void setDefault(GpgSignatureVerifierFactory factory) { + defaultFactory = factory; } - return null; } /** @@ -48,7 +69,7 @@ public abstract class GpgSignatureVerifierFactory { * @return the default factory or {@code null} if none set */ public static GpgSignatureVerifierFactory getDefault() { - return defaultFactory; + return DefaultFactory.getDefault(); } /** @@ -58,7 +79,7 @@ public abstract class GpgSignatureVerifierFactory { * the new default factory */ public static void setDefault(GpgSignatureVerifierFactory factory) { - defaultFactory = factory; + DefaultFactory.setDefault(factory); } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java index 5b32cf0b5f..b25a61b506 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, Salesforce. and others + * Copyright (C) 2018, 2022 Salesforce and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -26,22 +26,38 @@ import org.slf4j.LoggerFactory; * @since 5.3 */ public abstract class GpgSigner { + private static final Logger LOG = LoggerFactory.getLogger(GpgSigner.class); - private static GpgSigner defaultSigner = loadGpgSigner(); + private static class DefaultSigner { + + private static volatile GpgSigner defaultSigner = loadGpgSigner(); - private static GpgSigner loadGpgSigner() { - try { - ServiceLoader<GpgSigner> loader = ServiceLoader - .load(GpgSigner.class); - Iterator<GpgSigner> iter = loader.iterator(); - if (iter.hasNext()) { - return iter.next(); + private static GpgSigner loadGpgSigner() { + try { + ServiceLoader<GpgSigner> loader = ServiceLoader + .load(GpgSigner.class); + Iterator<GpgSigner> iter = loader.iterator(); + if (iter.hasNext()) { + return iter.next(); + } + } catch (ServiceConfigurationError e) { + LOG.error(e.getMessage(), e); } - } catch (ServiceConfigurationError e) { - LOG.error(e.getMessage(), e); + return null; + } + + private DefaultSigner() { + // No instantiation + } + + public static GpgSigner getDefault() { + return defaultSigner; + } + + public static void setDefault(GpgSigner signer) { + defaultSigner = signer; } - return null; } /** @@ -50,7 +66,7 @@ public abstract class GpgSigner { * @return the default signer, or <code>null</code>. */ public static GpgSigner getDefault() { - return defaultSigner; + return DefaultSigner.getDefault(); } /** @@ -61,7 +77,7 @@ public abstract class GpgSigner { * default. */ public static void setDefault(GpgSigner signer) { - GpgSigner.defaultSigner = signer; + DefaultSigner.setDefault(signer); } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommit.java index 94e7c53adb..c11fca13d8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommit.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommit.java @@ -138,6 +138,7 @@ public class PlotCommit<L extends PlotLane> extends RevCommit { * the commit to test. * @return true if the given commit built on top of this commit. */ + @SuppressWarnings("ReferenceEquality") public final boolean isChild(PlotCommit c) { for (PlotCommit a : children) if (a == c) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java index 18ea7560fd..458f240982 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java @@ -92,6 +92,7 @@ public class PlotCommitList<L extends PlotLane> extends } /** {@inheritDoc} */ + @SuppressWarnings("ReferenceEquality") @Override protected void enter(int index, PlotCommit<L> currCommit) { setupChildren(currCommit); @@ -188,6 +189,7 @@ public class PlotCommitList<L extends PlotLane> extends * may be null if <code>currCommit</code> is the first commit on * the lane */ + @SuppressWarnings("ReferenceEquality") private void handleBlockedLanes(final int index, final PlotCommit currCommit, final PlotCommit childOnLane) { for (PlotCommit child : currCommit.children) { @@ -214,6 +216,7 @@ public class PlotCommitList<L extends PlotLane> extends } // Handles the case where currCommit is a non-first parent of the child + @SuppressWarnings("ReferenceEquality") private PlotLane handleMerge(final int index, final PlotCommit currCommit, final PlotCommit childOnLane, PlotCommit child, PlotLane laneToUse) { @@ -287,6 +290,7 @@ public class PlotCommitList<L extends PlotLane> extends * @param child * @param laneToContinue */ + @SuppressWarnings("ReferenceEquality") private void drawLaneToChild(final int commitIndex, PlotCommit child, PlotLane laneToContinue) { for (int r = commitIndex - 1; r >= 0; r--) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java index a50eaf1a8a..a25948e50b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java @@ -143,8 +143,19 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { */ static final int TOPO_QUEUED = 1 << 6; + /** + * Set on a RevCommit when a {@link TreeRevFilter} has been applied. + * <p> + * This flag is processed by the {@link RewriteGenerator} to check if a + * {@link TreeRevFilter} has been applied. + * + * @see TreeRevFilter + * @see RewriteGenerator + */ + static final int TREE_REV_FILTER_APPLIED = 1 << 7; + /** Number of flag bits we keep internal for our own use. See above flags. */ - static final int RESERVED_FLAGS = 7; + static final int RESERVED_FLAGS = 8; private static final int APP_FLAGS = -1 & ~((1 << RESERVED_FLAGS) - 1); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java index a928c2e79b..1adef07ad9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java @@ -24,14 +24,7 @@ import org.eclipse.jgit.errors.MissingObjectException; * commit that matched the revision walker's filters. * <p> * This generator is the second phase of a path limited revision walk and - * assumes it is receiving RevCommits from {@link TreeRevFilter}, - * after they have been fully buffered by {@link AbstractRevQueue}. The full - * buffering is necessary to allow the simple loop used within our own - * {@link #rewrite(RevCommit)} to pull completely through a strand of - * {@link RevWalk#REWRITE} colored commits and come up with a simplification - * that makes the DAG dense. Not fully buffering the commits first would cause - * this loop to abort early, due to commits not being parsed and colored - * correctly. + * assumes it is receiving RevCommits from {@link TreeRevFilter}. * * @see TreeRevFilter */ @@ -43,9 +36,12 @@ class RewriteGenerator extends Generator { private final Generator source; + private final FIFORevQueue pending; + RewriteGenerator(Generator s) { super(s.firstParent); source = s; + pending = new FIFORevQueue(s.firstParent); } @Override @@ -58,13 +54,23 @@ class RewriteGenerator extends Generator { return source.outputType() & ~NEEDS_REWRITE; } + @SuppressWarnings("ReferenceEquality") @Override RevCommit next() throws MissingObjectException, IncorrectObjectTypeException, IOException { - final RevCommit c = source.next(); + RevCommit c = pending.next(); + if (c == null) { - return null; + c = source.next(); + if (c == null) { + // We are done: Both the source generator and our internal list + // are completely exhausted. + return null; + } } + + applyFilterToParents(c); + boolean rewrote = false; final RevCommit[] pList = c.parents; final int nParents = pList.length; @@ -90,10 +96,41 @@ class RewriteGenerator extends Generator { return c; } - private RevCommit rewrite(RevCommit p) { + /** + * Makes sure that the {@link TreeRevFilter} has been applied to all parents + * of this commit by the previous {@link PendingGenerator}. + * + * @param c + * @throws MissingObjectException + * @throws IncorrectObjectTypeException + * @throws IOException + */ + private void applyFilterToParents(RevCommit c) + throws MissingObjectException, IncorrectObjectTypeException, + IOException { + for (RevCommit parent : c.parents) { + while ((parent.flags & RevWalk.TREE_REV_FILTER_APPLIED) == 0) { + + RevCommit n = source.next(); + + if (n != null) { + pending.add(n); + } else { + // Source generator is exhausted; filter has been applied to + // all commits + return; + } + + } + + } + } + + private RevCommit rewrite(RevCommit p) throws MissingObjectException, + IncorrectObjectTypeException, IOException { for (;;) { - final RevCommit[] pList = p.parents; - if (pList.length > 1) { + + if (p.parents.length > 1) { // This parent is a merge, so keep it. // return p; @@ -113,14 +150,16 @@ class RewriteGenerator extends Generator { return p; } - if (pList.length == 0) { + if (p.parents.length == 0) { // We can't go back any further, other than to // just delete the parent entirely. // return null; } - p = pList[0]; + applyFilterToParents(p.parents[0]); + p = p.parents[0]; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java index bfcea6ea8f..a79901ca10 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java @@ -125,12 +125,6 @@ class StartGenerator extends Generator { } if ((g.outputType() & NEEDS_REWRITE) != 0) { - // Correction for an upstream NEEDS_REWRITE is to buffer - // fully and then apply a rewrite generator that can - // pull through the rewrite chain and produce a dense - // output graph. - // - g = new FIFORevQueue(g); g = new RewriteGenerator(g); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java index 822fc5320c..92d72268d1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java @@ -41,6 +41,8 @@ public class TreeRevFilter extends RevFilter { private static final int UNINTERESTING = RevWalk.UNINTERESTING; + private static final int FILTER_APPLIED = RevWalk.TREE_REV_FILTER_APPLIED; + private final int rewriteFlag; private final TreeWalk pathFilter; @@ -101,6 +103,7 @@ public class TreeRevFilter extends RevFilter { public boolean include(RevWalk walker, RevCommit c) throws StopWalkException, MissingObjectException, IncorrectObjectTypeException, IOException { + c.flags |= FILTER_APPLIED; // Reset the tree filter to scan this commit and parents. // RevCommit[] pList = c.parents; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java index 2443c4e771..cba5e1697c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java @@ -20,7 +20,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.text.MessageFormat; @@ -37,15 +36,11 @@ import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FileUtils; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.RawParseUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * The configuration file that is stored in the file of the file system. */ public class FileBasedConfig extends StoredConfig { - private static final Logger LOG = LoggerFactory - .getLogger(FileBasedConfig.class); private final File configFile; @@ -115,16 +110,15 @@ public class FileBasedConfig extends StoredConfig { */ @Override public void load() throws IOException, ConfigInvalidException { - final int maxRetries = 5; - int retryDelayMillis = 20; - int retries = 0; - while (true) { - final FileSnapshot oldSnapshot = snapshot; - final FileSnapshot newSnapshot; - // don't use config in this snapshot to avoid endless recursion - newSnapshot = FileSnapshot.saveNoConfig(getFile()); - try { - final byte[] in = IO.readFully(getFile()); + try { + FileSnapshot[] lastSnapshot = { null }; + Boolean wasRead = FileUtils.readWithRetries(getFile(), f -> { + final FileSnapshot oldSnapshot = snapshot; + final FileSnapshot newSnapshot; + // don't use config in this snapshot to avoid endless recursion + newSnapshot = FileSnapshot.saveNoConfig(f); + lastSnapshot[0] = newSnapshot; + final byte[] in = IO.readFully(f); final ObjectId newHash = hash(in); if (hash.equals(newHash)) { if (oldSnapshot.equals(newSnapshot)) { @@ -145,47 +139,17 @@ public class FileBasedConfig extends StoredConfig { snapshot = newSnapshot; hash = newHash; } - return; - } catch (FileNotFoundException noFile) { - // might be locked by another process (see exception Javadoc) - if (retries < maxRetries && configFile.exists()) { - if (LOG.isDebugEnabled()) { - LOG.debug(MessageFormat.format( - JGitText.get().configHandleMayBeLocked, - Integer.valueOf(retries)), noFile); - } - try { - Thread.sleep(retryDelayMillis); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - retries++; - retryDelayMillis *= 2; // max wait 1260 ms - continue; - } - if (configFile.exists()) { - throw noFile; - } + return Boolean.TRUE; + }); + if (wasRead == null) { clear(); - snapshot = newSnapshot; - return; - } catch (IOException e) { - if (FileUtils.isStaleFileHandle(e) - && retries < maxRetries) { - if (LOG.isDebugEnabled()) { - LOG.debug(MessageFormat.format( - JGitText.get().configHandleIsStale, - Integer.valueOf(retries)), e); - } - retries++; - continue; - } - throw new IOException(MessageFormat - .format(JGitText.get().cannotReadFile, getFile()), e); - } catch (ConfigInvalidException e) { - throw new ConfigInvalidException(MessageFormat - .format(JGitText.get().cannotReadFile, getFile()), e); + snapshot = lastSnapshot[0]; } + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new ConfigInvalidException(MessageFormat + .format(JGitText.get().cannotReadFile, getFile()), e); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java index f48e1e68cc..3f167ccce2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java @@ -1,7 +1,7 @@ /* * Copyright (C) 2008, 2010 Google Inc. * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> - * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others + * Copyright (C) 2008, 2022 Shawn O. Pearce <spearce@spearce.org> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -1004,9 +1004,12 @@ public abstract class BasePackFetchConnection extends BasePackConnection OutputStream outputStream) throws IOException { onReceivePack(); InputStream input = in; - if (sideband) - input = new SideBandInputStream(input, monitor, getMessageWriter(), - outputStream); + SideBandInputStream sidebandIn = null; + if (sideband) { + sidebandIn = new SideBandInputStream(input, monitor, + getMessageWriter(), outputStream); + input = sidebandIn; + } try (ObjectInserter ins = local.newObjectInserter()) { PackParser parser = ins.newPackParser(input); @@ -1015,6 +1018,10 @@ public abstract class BasePackFetchConnection extends BasePackConnection parser.setLockMessage(lockMessage); packLock = parser.parse(monitor); ins.flush(); + } finally { + if (sidebandIn != null) { + sidebandIn.drainMessages(); + } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java index b87a85d934..b7be59d6f8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> - * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others + * Copyright (C) 2008, 2022 Shawn O. Pearce <spearce@spearce.org> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -194,10 +194,11 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen // the other data channels. // int b = in.read(); - if (0 <= b) + if (0 <= b) { throw new TransportException(uri, MessageFormat.format( JGitText.get().expectedEOFReceived, Character.valueOf((char) b))); + } } } } catch (TransportException e) { @@ -205,6 +206,9 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen } catch (Exception e) { throw new TransportException(uri, e.getMessage(), e); } finally { + if (in instanceof SideBandInputStream) { + ((SideBandInputStream) in).drainMessages(); + } close(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java index 7d7b3ee0a0..87e5476036 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java @@ -31,6 +31,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.NotSupportedException; @@ -56,6 +57,12 @@ class FetchProcess { /** List of things we want to fetch from the remote repository. */ private final Collection<RefSpec> toFetch; + /** + * List of things we don't want to fetch from the remote repository or to + * the local repository. + */ + private final Collection<RefSpec> negativeRefSpecs; + /** Set of refs we will actually wind up asking to obtain. */ private final HashMap<ObjectId, Ref> askFor = new HashMap<>(); @@ -74,9 +81,12 @@ class FetchProcess { private Map<String, Ref> localRefs; - FetchProcess(Transport t, Collection<RefSpec> f) { + FetchProcess(Transport t, Collection<RefSpec> refSpecs) { transport = t; - toFetch = f; + toFetch = refSpecs.stream().filter(refSpec -> !refSpec.isNegative()) + .collect(Collectors.toList()); + negativeRefSpecs = refSpecs.stream().filter(RefSpec::isNegative) + .collect(Collectors.toList()); } void execute(ProgressMonitor monitor, FetchResult result, @@ -389,8 +399,13 @@ class FetchProcess { private void expandWildcard(RefSpec spec, Set<Ref> matched) throws TransportException { for (Ref src : conn.getRefs()) { - if (spec.matchSource(src) && matched.add(src)) - want(src, spec.expandFromSource(src)); + if (spec.matchSource(src)) { + RefSpec expandedRefSpec = spec.expandFromSource(src); + if (!matchNegativeRefSpec(expandedRefSpec) + && matched.add(src)) { + want(src, expandedRefSpec); + } + } } } @@ -406,11 +421,27 @@ class FetchProcess { if (src == null) { throw new TransportException(MessageFormat.format(JGitText.get().remoteDoesNotHaveSpec, want)); } - if (matched.add(src)) { + if (!matchNegativeRefSpec(spec) && matched.add(src)) { want(src, spec); } } + private boolean matchNegativeRefSpec(RefSpec spec) { + for (RefSpec negativeRefSpec : negativeRefSpecs) { + if (negativeRefSpec.getSource() != null && spec.getSource() != null + && negativeRefSpec.matchSource(spec.getSource())) { + return true; + } + + if (negativeRefSpec.getDestination() != null + && spec.getDestination() != null && negativeRefSpec + .matchDestination(spec.getDestination())) { + return true; + } + } + return false; + } + private boolean localHasObject(ObjectId id) throws TransportException { try { return transport.local.getObjectDatabase().has(id); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java index 942dad46e0..b59ae0c450 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> and others + * Copyright (C) 2008, 2022 Marek Zawirski <marek.zawirski@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 @@ -166,6 +166,7 @@ class PushProcess { if (prePush != null) { try { prePush.setRefs(willBeAttempted); + prePush.setDryRun(transport.isDryRun()); prePush.call(); } catch (AbortedByHookException | IOException e) { throw new TransportException(e.getMessage(), e); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java index 56d0036a20..e36eeccfb1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java @@ -53,6 +53,9 @@ public class RefSpec implements Serializable { /** Is this the special ":" RefSpec? */ private boolean matching; + /** Is this a negative refspec. */ + private boolean negative; + /** * How strict to be about wildcards. * @@ -96,12 +99,23 @@ public class RefSpec implements Serializable { wildcard = false; srcName = Constants.HEAD; dstName = null; + negative =false; allowMismatchedWildcards = WildcardMode.REQUIRE_MATCH; } /** * Parse a ref specification for use during transport operations. * <p> + * {@link RefSpec}s can be regular or negative, regular RefSpecs indicate + * what to include in transport operations while negative RefSpecs indicate + * what to exclude in fetch. + * <p> + * Negative {@link RefSpec}s can't be force, must have only source or + * destination. Wildcard patterns are also supported in negative RefSpecs + * but they can not go with {@code WildcardMode.REQUIRE_MATCH} because they + * are natually one to many mappings. + * + * <p> * Specifications are typically one of the following forms: * <ul> * <li><code>refs/heads/master</code></li> @@ -121,6 +135,12 @@ public class RefSpec implements Serializable { * <li><code>refs/heads/*:refs/heads/master</code></li> * </ul> * + * Negative specifications are usually like: + * <ul> + * <li><code>^:refs/heads/master</code></li> + * <li><code>^refs/heads/*</code></li> + * </ul> + * * @param spec * string describing the specification. * @param mode @@ -133,11 +153,22 @@ public class RefSpec implements Serializable { public RefSpec(String spec, WildcardMode mode) { this.allowMismatchedWildcards = mode; String s = spec; + + if (s.startsWith("^+") || s.startsWith("+^")) { + throw new IllegalArgumentException( + JGitText.get().invalidNegativeAndForce); + } + if (s.startsWith("+")) { //$NON-NLS-1$ force = true; s = s.substring(1); } + if(s.startsWith("^")) { + negative = true; + s = s.substring(1); + } + boolean matchPushSpec = false; final int c = s.lastIndexOf(':'); if (c == 0) { @@ -181,6 +212,21 @@ public class RefSpec implements Serializable { } srcName = checkValid(s); } + + // Negative refspecs must only have dstName or srcName. + if (isNegative()) { + if (isNullOrEmpty(srcName) && isNullOrEmpty(dstName)) { + throw new IllegalArgumentException(MessageFormat + .format(JGitText.get().invalidRefSpec, spec)); + } + if (!isNullOrEmpty(srcName) && !isNullOrEmpty(dstName)) { + throw new IllegalArgumentException(MessageFormat + .format(JGitText.get().invalidRefSpec, spec)); + } + if(wildcard && mode == WildcardMode.REQUIRE_MATCH) { + throw new IllegalArgumentException(MessageFormat + .format(JGitText.get().invalidRefSpec, spec));} + } matching = matchPushSpec; } @@ -205,13 +251,15 @@ public class RefSpec implements Serializable { * the specification is invalid. */ public RefSpec(String spec) { - this(spec, WildcardMode.REQUIRE_MATCH); + this(spec, spec.startsWith("^") ? WildcardMode.ALLOW_MISMATCH + : WildcardMode.REQUIRE_MATCH); } private RefSpec(RefSpec p) { matching = false; force = p.isForceUpdate(); wildcard = p.isWildcard(); + negative = p.isNegative(); srcName = p.getSource(); dstName = p.getDestination(); allowMismatchedWildcards = p.allowMismatchedWildcards; @@ -246,6 +294,10 @@ public class RefSpec implements Serializable { */ public RefSpec setForceUpdate(boolean forceUpdate) { final RefSpec r = new RefSpec(this); + if (forceUpdate && isNegative()) { + throw new IllegalArgumentException( + JGitText.get().invalidNegativeAndForce); + } r.matching = matching; r.force = forceUpdate; return r; @@ -265,6 +317,16 @@ public class RefSpec implements Serializable { } /** + * Check if this specification is a negative one. + * + * @return true if this specification is negative. + * @since 6.2 + */ + public boolean isNegative() { + return negative; + } + + /** * Get the source ref description. * <p> * During a fetch this is the name of the ref on the remote repository we @@ -435,6 +497,10 @@ public class RefSpec implements Serializable { return this; } + private static boolean isNullOrEmpty(String refName) { + return refName == null || refName.isEmpty(); + } + /** * Expand this specification to exactly match a ref. * <p> @@ -570,6 +636,9 @@ public class RefSpec implements Serializable { if (isForceUpdate() != b.isForceUpdate()) { return false; } + if(isNegative() != b.isNegative()) { + return false; + } if (isMatching()) { return b.isMatching(); } else if (b.isMatching()) { @@ -587,6 +656,9 @@ public class RefSpec implements Serializable { if (isForceUpdate()) { r.append('+'); } + if(isNegative()) { + r.append('^'); + } if (isMatching()) { r.append(':'); } else { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java index 2f3160bb8e..c4e105ec4a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java @@ -16,10 +16,7 @@ import java.io.Serializable; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import org.eclipse.jgit.lib.Config; @@ -54,10 +51,6 @@ public class RemoteConfig implements Serializable { private static final String KEY_TIMEOUT = "timeout"; //$NON-NLS-1$ - private static final String KEY_INSTEADOF = "insteadof"; //$NON-NLS-1$ - - private static final String KEY_PUSHINSTEADOF = "pushinsteadof"; //$NON-NLS-1$ - private static final boolean DEFAULT_MIRROR = false; /** Default value for {@link #getUploadPack()} if not specified. */ @@ -135,10 +128,10 @@ public class RemoteConfig implements Serializable { String val; vlst = rc.getStringList(SECTION, name, KEY_URL); - Map<String, String> insteadOf = getReplacements(rc, KEY_INSTEADOF); + UrlConfig urls = new UrlConfig(rc); uris = new ArrayList<>(vlst.length); for (String s : vlst) { - uris.add(new URIish(replaceUri(s, insteadOf))); + uris.add(new URIish(urls.replace(s))); } String[] plst = rc.getStringList(SECTION, name, KEY_PUSHURL); pushURIs = new ArrayList<>(plst.length); @@ -148,11 +141,9 @@ public class RemoteConfig implements Serializable { if (pushURIs.isEmpty()) { // Would default to the uris. If we have pushinsteadof, we must // supply rewritten push uris. - Map<String, String> pushInsteadOf = getReplacements(rc, - KEY_PUSHINSTEADOF); - if (!pushInsteadOf.isEmpty()) { + if (urls.hasPushReplacements()) { for (String s : vlst) { - String replaced = replaceUri(s, pushInsteadOf); + String replaced = urls.replacePush(s); if (!s.equals(replaced)) { pushURIs.add(new URIish(replaced)); } @@ -248,39 +239,6 @@ public class RemoteConfig implements Serializable { rc.unset(SECTION, getName(), key); } - private Map<String, String> getReplacements(final Config config, - final String keyName) { - final Map<String, String> replacements = new HashMap<>(); - for (String url : config.getSubsections(KEY_URL)) - for (String insteadOf : config.getStringList(KEY_URL, url, keyName)) - replacements.put(insteadOf, url); - return replacements; - } - - private String replaceUri(final String uri, - final Map<String, String> replacements) { - if (replacements.isEmpty()) { - return uri; - } - Entry<String, String> match = null; - for (Entry<String, String> replacement : replacements.entrySet()) { - // Ignore current entry if not longer than previous match - if (match != null - && match.getKey().length() > replacement.getKey() - .length()) { - continue; - } - if (!uri.startsWith(replacement.getKey())) { - continue; - } - match = replacement; - } - if (match != null) { - return match.getValue() + uri.substring(match.getKey().length()); - } - return uri; - } - /** * Get the local name this remote configuration is recognized as. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java index 8a8d977ed3..96c7be5b97 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> - * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others + * Copyright (C) 2008, 2022 Shawn O. Pearce <spearce@spearce.org> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -28,6 +28,8 @@ import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.RawParseUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Unmultiplexes the data portion of a side-band channel. @@ -46,6 +48,10 @@ import org.eclipse.jgit.util.RawParseUtils; * @since 4.11 */ public class SideBandInputStream extends InputStream { + + private static final Logger LOG = LoggerFactory + .getLogger(SideBandInputStream.class); + static final int CH_DATA = 1; static final int CH_PROGRESS = 2; static final int CH_ERROR = 3; @@ -210,6 +216,21 @@ public class SideBandInputStream extends InputStream { monitor.beginTask(remote(currentTask), totalWorkUnits); } + /** + * Forces any buffered progress messages to be written. + */ + void drainMessages() { + if (!progressBuffer.isEmpty()) { + try { + progress("\n"); //$NON-NLS-1$ + } catch (IOException e) { + // Just log; otherwise this IOException might hide a real + // TransportException + LOG.error(e.getMessage(), e); + } + } + } + private static String remote(String msg) { String prefix = JGitText.get().prefixRemote; StringBuilder r = new StringBuilder(prefix.length() + msg.length() + 1); 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 1e98a56f79..a0194ea8b1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> - * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others + * Copyright (C) 2008, 2022 Shawn O. Pearce <spearce@spearce.org> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -36,15 +36,35 @@ import org.eclipse.jgit.util.SystemReader; */ public abstract class SshSessionFactory { - private static volatile SshSessionFactory INSTANCE = loadSshSessionFactory(); + private static class DefaultFactory { - private static SshSessionFactory loadSshSessionFactory() { - ServiceLoader<SshSessionFactory> loader = ServiceLoader.load(SshSessionFactory.class); - Iterator<SshSessionFactory> iter = loader.iterator(); - if(iter.hasNext()) { - return iter.next(); + private static volatile SshSessionFactory INSTANCE = loadSshSessionFactory(); + + private static SshSessionFactory loadSshSessionFactory() { + ServiceLoader<SshSessionFactory> loader = ServiceLoader + .load(SshSessionFactory.class); + Iterator<SshSessionFactory> iter = loader.iterator(); + if (iter.hasNext()) { + return iter.next(); + } + return null; + } + + private DefaultFactory() { + // No instantiation + } + + public static SshSessionFactory getInstance() { + return INSTANCE; + } + + public static void setInstance(SshSessionFactory newFactory) { + if (newFactory != null) { + INSTANCE = newFactory; + } else { + INSTANCE = loadSshSessionFactory(); + } } - return null; } /** @@ -57,7 +77,7 @@ public abstract class SshSessionFactory { * @return factory the current factory for this JVM. */ public static SshSessionFactory getInstance() { - return INSTANCE; + return DefaultFactory.getInstance(); } /** @@ -68,11 +88,7 @@ public abstract class SshSessionFactory { * {@code null} the default factory will be restored. */ public static void setInstance(SshSessionFactory newFactory) { - if (newFactory != null) { - INSTANCE = newFactory; - } else { - INSTANCE = loadSshSessionFactory(); - } + DefaultFactory.setInstance(newFactory); } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java index 0eab4434ed..3222d6330c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java @@ -1230,7 +1230,9 @@ public abstract class Transport implements AutoCloseable { * @param toFetch * specification of refs to fetch locally. May be null or the * empty collection to use the specifications from the - * RemoteConfig. Source for each RefSpec can't be null. + * RemoteConfig. May contains regular and negative + * {@link RefSpec}s. Source for each regular RefSpec can't + * be null. * @return information describing the tracking refs updated. * @throws org.eclipse.jgit.errors.NotSupportedException * this transport implementation does not support fetching @@ -1264,7 +1266,9 @@ public abstract class Transport implements AutoCloseable { * @param toFetch * specification of refs to fetch locally. May be null or the * empty collection to use the specifications from the - * RemoteConfig. Source for each RefSpec can't be null. + * RemoteConfig. May contains regular and negative + * {@link RefSpec}s. Source for each regular RefSpec can't + * be null. * @param branch * the initial branch to check out when cloning the repository. * Can be specified as ref name (<code>refs/heads/master</code>), diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UrlConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UrlConfig.java new file mode 100644 index 0000000000..574fcf806d --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UrlConfig.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.transport; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.jgit.lib.Config; + +/** + * Support for URL translations via git configs {@code url.<base>.insteadOf} and + * {@code url.<base>.pushInsteadOf}. + * + * @since 6.2 + */ +public class UrlConfig { + + private static final String KEY_INSTEADOF = "insteadof"; //$NON-NLS-1$ + + private static final String KEY_PUSHINSTEADOF = "pushinsteadof"; //$NON-NLS-1$ + + private static final String SECTION_URL = "url"; //$NON-NLS-1$ + + private final Config config; + + private Map<String, String> insteadOf; + + private Map<String, String> pushInsteadOf; + + /** + * Creates a new {@link UrlConfig} instance. + * + * @param config + * {@link Config} to read values from + */ + public UrlConfig(Config config) { + this.config = config; + } + + /** + * Performs replacements as defined by git config + * {@code url.<base>.insteadOf}. If there is no match, the input is returned + * unchanged. + * + * @param url + * to substitute + * @return the {@code url} with substitution applied + */ + public String replace(String url) { + if (insteadOf == null) { + insteadOf = load(KEY_INSTEADOF); + } + return replace(url, insteadOf); + } + + /** + * Tells whether there are push replacements. + * + * @return {@code true} if there are push replacements, {@code false} + * otherwise + */ + public boolean hasPushReplacements() { + if (pushInsteadOf == null) { + pushInsteadOf = load(KEY_PUSHINSTEADOF); + } + return !pushInsteadOf.isEmpty(); + } + + /** + * Performs replacements as defined by git config + * {@code url.<base>.pushInsteadOf}. If there is no match, the input is + * returned unchanged. + * + * @param url + * to substitute + * @return the {@code url} with substitution applied + */ + public String replacePush(String url) { + if (pushInsteadOf == null) { + pushInsteadOf = load(KEY_PUSHINSTEADOF); + } + return replace(url, pushInsteadOf); + } + + private Map<String, String> load(String key) { + Map<String, String> replacements = new HashMap<>(); + for (String url : config.getSubsections(SECTION_URL)) { + for (String prefix : config.getStringList(SECTION_URL, url, key)) { + replacements.put(prefix, url); + } + } + return replacements; + } + + private String replace(String uri, Map<String, String> replacements) { + Entry<String, String> match = null; + for (Entry<String, String> replacement : replacements.entrySet()) { + // Ignore current entry if not longer than previous match + if (match != null && match.getKey().length() > replacement.getKey() + .length()) { + continue; + } + if (uri.startsWith(replacement.getKey())) { + match = replacement; + } + } + if (match != null) { + return match.getValue() + uri.substring(match.getKey().length()); + } + return uri; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/Equality.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Equality.java new file mode 100644 index 0000000000..da1684630b --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/Equality.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022, Fabio Ponciroli <ponch78@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.util; + +/** + * Equality utilities. + * + * @since: 6.2 + */ +public class Equality { + + /** + * Compare by reference + * + * @param a + * First object to compare + * @param b + * Second object to compare + * @return {@code true} if the objects are identical, {@code false} + * otherwise + * + * @since 6.2 + */ + @SuppressWarnings("ReferenceEquality") + public static <T> boolean isSameInstance(T a, T b) { + return a == b; + } +}
\ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java index b9dd9baa61..f013e7e095 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java @@ -17,6 +17,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InterruptedIOException; import java.nio.channels.FileChannel; import java.nio.file.AtomicMoveNotSupportedException; import java.nio.file.CopyOption; @@ -655,6 +656,99 @@ public class FileUtils { } /** + * Like a {@link java.util.function.Function} but throwing an + * {@link Exception}. + * + * @param <A> + * input type + * @param <B> + * output type + * @since 6.2 + */ + @FunctionalInterface + public interface IOFunction<A, B> { + + /** + * Performs the function. + * + * @param t + * input to operate on + * @return the output + * @throws Exception + * if a problem occurs + */ + B apply(A t) throws Exception; + } + + private static void backOff(long delay, IOException cause) + throws IOException { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + IOException interruption = new InterruptedIOException(); + interruption.initCause(e); + interruption.addSuppressed(cause); + Thread.currentThread().interrupt(); // Re-set flag + throw interruption; + } + } + + /** + * Invokes the given {@link IOFunction}, performing a limited number of + * re-tries if exceptions occur that indicate either a stale NFS file handle + * or that indicate that the file may be written concurrently. + * + * @param <T> + * result type + * @param file + * to read + * @param reader + * for reading the file and creating an instance of {@code T} + * @return the result of the {@code reader}, or {@code null} if the file + * does not exist + * @throws Exception + * if a problem occurs + * @since 6.2 + */ + public static <T> T readWithRetries(File file, + IOFunction<File, ? extends T> reader) + throws Exception { + int maxStaleRetries = 5; + int retries = 0; + long backoff = 50; + while (true) { + try { + try { + return reader.apply(file); + } catch (IOException e) { + if (FileUtils.isStaleFileHandleInCausalChain(e) + && retries < maxStaleRetries) { + if (LOG.isDebugEnabled()) { + LOG.debug(MessageFormat.format( + JGitText.get().packedRefsHandleIsStale, + Integer.valueOf(retries)), e); + } + retries++; + continue; + } + throw e; + } + } catch (FileNotFoundException noFile) { + if (!file.isFile()) { + return null; + } + // Probably Windows and some other thread is writing the file + // concurrently. + if (backoff > 1000) { + throw noFile; + } + backOff(backoff, noFile); + backoff *= 2; // 50, 100, 200, 400, 800 ms + } + } + } + + /** * @param file * @return {@code true} if the passed file is a symbolic link */ @@ -18,7 +18,7 @@ <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> <packaging>pom</packaging> - <version>6.1.1-SNAPSHOT</version> + <version>6.2.0-SNAPSHOT</version> <name>JGit - Parent</name> <url>${jgit-url}</url> @@ -150,11 +150,11 @@ <java.version>11</java.version> <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest> - <jgit-last-release-version>6.0.0.202111291000-r</jgit-last-release-version> + <jgit-last-release-version>6.1.0.202203080745-r</jgit-last-release-version> <ant-version>1.10.12</ant-version> <apache-sshd-version>2.8.0</apache-sshd-version> <jsch-version>0.1.55</jsch-version> - <jzlib-version>1.1.1</jzlib-version> + <jzlib-version>1.1.3</jzlib-version> <javaewah-version>1.1.13</javaewah-version> <junit-version>4.13.2</junit-version> <test-fork-count>1C</test-fork-count> @@ -168,7 +168,7 @@ <httpcore-version>4.4.14</httpcore-version> <slf4j-version>1.7.30</slf4j-version> <maven-javadoc-plugin-version>3.3.1</maven-javadoc-plugin-version> - <tycho-extras-version>2.5.0</tycho-extras-version> + <tycho-extras-version>2.6.0</tycho-extras-version> <gson-version>2.8.9</gson-version> <bouncycastle-version>1.70</bouncycastle-version> <spotbugs-maven-plugin-version>4.3.0</spotbugs-maven-plugin-version> |