diff options
291 files changed, 10225 insertions, 2327 deletions
@@ -3,8 +3,33 @@ build --repository_cache=~/.gerritcodereview/bazel-cache/repository build --experimental_strict_action_env build --action_env=PATH build --disk_cache=~/.gerritcodereview/bazel-cache/cas -build --java_toolchain //tools:error_prone_warnings_toolchain + +# Builds using remote_jdk11, executes using remote_jdk11 or local_jdk +build --java_language_version=11 +build --java_runtime_version=remotejdk_11 +build --tool_java_language_version=11 +build --tool_java_runtime_version=remotejdk_11 + +# Builds and executes on RBE using remotejdk_11 +build:remote --java_language_version=11 +build:remote --java_runtime_version=remotejdk_11 +build:remote --tool_java_language_version=11 +build:remote --tool_java_runtime_version=remotejdk_11 + +# Builds using remote_jdk17, executes using remote_jdk11 or local_jdk +build:java17 --java_language_version=17 +build:java17 --java_runtime_version=remotejdk_17 +build:java17 --tool_java_language_version=17 +build:java17 --tool_java_runtime_version=remotejdk_17 + +# Builds and executes on RBE using remotejdk_17 +build:remote17 --java_language_version=17 +build:remote17 --java_runtime_version=remotejdk_17 +build:remote17 --tool_java_language_version=17 +build:remote17 --tool_java_runtime_version=remotejdk_17 test --build_tests_only test --test_output=errors +import %workspace%/tools/remote-bazelrc + diff --git a/.bazelversion b/.bazelversion index fcdb2e109f..0062ac9718 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -4.0.0 +5.0.0 @@ -12,6 +12,7 @@ Roberto Tyley <roberto.tyley@guardian.co.uk> roberto <roberto.tyl SaÅ¡a Živkov <sasa.zivkov@sap.com> Sasa Zivkov <sasa.zivkov@sap.com> SaÅ¡a Živkov <sasa.zivkov@sap.com> SaÅ¡a Živkov <zivkov@gmail.com> SaÅ¡a Živkov <sasa.zivkov@sap.com> Sasa Zivkov <zivkov@gmail.com> +Sebastian Schuberth <sschuberth@gmail.com> Sebastian Schuberth <sebastian.schuberth@bosch.io> Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <sop@google.com> Shawn Pearce <spearce@spearce.org> Shawn Pearce <sop@google.com> Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <spearce@spearce.org> diff --git a/DEPENDENCIES b/DEPENDENCIES index ebbe4805aa..93fa850d8a 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,69 +1,68 @@ maven/mavencentral/args4j/args4j/2.33, MIT, approved, CQ11068 -maven/mavencentral/com.google.code.gson/gson/2.8.7, Apache-2.0, approved, CQ23496 -maven/mavencentral/com.googlecode.javaewah/JavaEWAH/1.1.12, Apache-2.0, approved, CQ11658 +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/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/3.1.0, Apache-2.0 AND (CDDL-1.1 OR GPL-2.0 WITH Classpath-exception-2.0), approved, CQ7248 -maven/mavencentral/junit/junit/4.13, , approved, CQ22796 -maven/mavencentral/log4j/log4j/1.2.15, Apache-2.0, approved, CQ7837 +maven/mavencentral/javax.servlet/javax.servlet-api/4.0.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 maven/mavencentral/net.i2p.crypto/eddsa/0.3.0, CC0-1.0, approved, CQ22537 +maven/mavencentral/net.java.dev.jna/jna-platform/5.8.0, Apache-2.0 OR LGPL-2.1-or-later, approved, CQ23218 +maven/mavencentral/net.java.dev.jna/jna/5.8.0, Apache-2.0 OR LGPL-2.1-or-later, approved, CQ23217 maven/mavencentral/net.sf.jopt-simple/jopt-simple/4.6, MIT, approved, clearlydefined -maven/mavencentral/org.apache.ant/ant-launcher/1.10.10, Apache-2.0 AND W3C AND LicenseRef-Public-Domain, approved, CQ15560 -maven/mavencentral/org.apache.ant/ant/1.10.10, Apache-2.0 AND W3C AND LicenseRef-Public-Domain, approved, CQ15560 -maven/mavencentral/org.apache.commons/commons-compress/1.20, Apache-2.0 AND BSD-3-Clause AND LicenseRef-Public-Domain, approved, CQ21771 +maven/mavencentral/org.apache.ant/ant-launcher/1.10.12, Apache-2.0 AND W3C AND LicenseRef-Public-Domain, approved, CQ15560 +maven/mavencentral/org.apache.ant/ant/1.10.12, Apache-2.0 AND W3C AND LicenseRef-Public-Domain, approved, CQ15560 +maven/mavencentral/org.apache.commons/commons-compress/1.21, Apache-2.0 AND BSD-3-Clause AND bzip2-1.0.6 AND LicenseRef-Public-Domain, approved, CQ23710 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.7.0, Apache-2.0 and ISC, approved, CQ23469 -maven/mavencentral/org.apache.sshd/sshd-core/2.7.0, Apache-2.0, approved, CQ23469 -maven/mavencentral/org.apache.sshd/sshd-osgi/2.7.0, Apache-2.0 and ISC, approved, CQ23469 -maven/mavencentral/org.apache.sshd/sshd-sftp/2.7.0, Apache-2.0, approved, CQ23470 +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 -maven/mavencentral/org.bouncycastle/bcpg-jdk15on/1.69, MIT and Apache-2.0, approved, CQ23472 -maven/mavencentral/org.bouncycastle/bcpkix-jdk15on/1.69, MIT, approved, CQ23473 -maven/mavencentral/org.bouncycastle/bcprov-jdk15on/1.69, MIT, approved, CQ23471 -maven/mavencentral/org.bouncycastle/bcutil-jdk15on/1.69, MIT, approved, CQ23474 -maven/mavencentral/org.eclipse.jetty/jetty-http/9.4.43.v20210629, , approved, eclipse -maven/mavencentral/org.eclipse.jetty/jetty-io/9.4.43.v20210629, , approved, eclipse -maven/mavencentral/org.eclipse.jetty/jetty-security/9.4.43.v20210629, , approved, eclipse -maven/mavencentral/org.eclipse.jetty/jetty-server/9.4.43.v20210629, , approved, eclipse -maven/mavencentral/org.eclipse.jetty/jetty-servlet/9.4.43.v20210629, , approved, eclipse -maven/mavencentral/org.eclipse.jetty/jetty-util-ajax/9.4.43.v20210629, , approved, eclipse -maven/mavencentral/org.eclipse.jetty/jetty-util/9.4.43.v20210629, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ant.test/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ant/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.archive/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.gpg.bc/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.apache/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.server/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.http.test/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit.http/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit.ssh/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.junit/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.server.test/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.server/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs.test/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.lfs/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.pgm.test/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.pgm/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache.test/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.apache/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ssh.jsch/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.test/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit.ui/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.eclipse.jgit/org.eclipse.jgit/5.13.0-SNAPSHOT, , approved, eclipse -maven/mavencentral/org.hamcrest/hamcrest-core/1.3, BSD-2-Clause, approved, CQ7063 -maven/mavencentral/org.hamcrest/hamcrest/2.2, BSD-2-Clause, approved, clearlydefined -maven/mavencentral/org.mockito/mockito-core/2.23.0, MIT, approved, CQ17976 +maven/mavencentral/org.bouncycastle/bcpg-jdk15on/1.70, Apache-2.0, approved, #1713 +maven/mavencentral/org.bouncycastle/bcpkix-jdk15on/1.70, MIT, approved, clearlydefined +maven/mavencentral/org.bouncycastle/bcprov-jdk15on/1.70, MIT, approved, #1712 +maven/mavencentral/org.bouncycastle/bcutil-jdk15on/1.70, MIT, approved, clearlydefined +maven/mavencentral/org.eclipse.jetty.toolchain/jetty-servlet-api/4.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-http/10.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-io/10.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-security/10.0.6, EPL-2.0 OR Apache-2.0, approved, rt.jetty +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.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 -maven/mavencentral/org.openjdk.jmh/jmh-core/1.32, GPL-2.0, approved, CQ23499 -maven/mavencentral/org.openjdk.jmh/jmh-generator-annprocess/1.32, GPL-2.0, approved, CQ23500 -maven/mavencentral/org.osgi/org.osgi.core/4.3.1, Apache-2.0, approved, CQ10111 -maven/mavencentral/org.slf4j/jcl-over-slf4j/1.7.30, Apache-2.0, approved, CQ12843 +maven/mavencentral/org.openjdk.jmh/jmh-core/1.32, GPL-2.0-only with Classpath-exception-2.0, approved, #959 +maven/mavencentral/org.openjdk.jmh/jmh-generator-annprocess/1.32, GPL-2.0-only with Classpath-exception-2.0, approved, #962 +maven/mavencentral/org.osgi/org.osgi.core/6.0.0, Apache-2.0, approved, #1794 +maven/mavencentral/org.slf4j/jcl-over-slf4j/1.7.32, Apache-2.0, approved, CQ12843 maven/mavencentral/org.slf4j/slf4j-api/1.7.30, MIT, approved, CQ13368 -maven/mavencentral/org.slf4j/slf4j-log4j12/1.7.30, MIT, approved, CQ7665 +maven/mavencentral/org.slf4j/slf4j-simple/1.7.30, MIT, approved, CQ7952 maven/mavencentral/org.tukaani/xz/1.9, LicenseRef-Public-Domain, approved, CQ23498 diff --git a/Documentation/config-options.md b/Documentation/config-options.md index 7221cceb35..815677c7d8 100644 --- a/Documentation/config-options.md +++ b/Documentation/config-options.md @@ -46,7 +46,8 @@ For details on native git options see also the official [git config documentatio | `core.streamFileThreshold` | `50 MiB` | ⃞ | The size threshold beyond which objects must be streamed. | | `core.supportsAtomicFileCreation` | `true` | ⃞ | Whether the filesystem supports atomic file creation. | | `core.symlinks` | Auto detect if filesystem supports symlinks| ✅ | If false, symbolic links are checked out as small plain files that contain the link text. | -| `core.trustFolderStat` | `true` | ⃞ | Whether to trust the pack folder's modification time. If `false` JGit will always scan the `.git/objects/pack` folder to check for new pack files. This can help to workaround caching issues on NFS, but reduces performance. If set to `true` it uses the `lastmodified` attribute of the folder and assumes that no new pack files can be in this folder if its modification time has not changed. | +| `core.trustFolderStat` | `true` | ⃞ | Whether to trust the pack folder's, packed-refs file's and loose-objects folder's file attributes (Java equivalent of stat command on *nix). When looking for pack files, if `false` JGit will always scan the `.git/objects/pack` folder and if set to `true` it assumes that pack files are unchanged if the file attributes of the pack folder are unchanged. When getting the list of packed refs, if `false` JGit will always read the packed-refs file and if set to `true` it uses the file attributes of the packed-refs file and will only read it if a file attribute has changed. When looking for loose objects, if `false` and if a loose object is not found, JGit will open and close a stream to `.git/objects` folder (which can refresh its directory listing, at least on some NFS clients) and retry looking for that loose object. Setting this option to `false` can help to workaround caching issues on NFS, but reduces performance. | +| `core.trustPackedRefsStat` | `unset` | ⃞ | Whether to trust the file attributes (Java equivalent of stat command on *nix) of the packed-refs file. If `never` JGit will ignore the file attributes of the packed-refs file and always read it. If `always` JGit will trust the file attributes of the packed-refs file and will only read it if a file attribute has changed. `after_open` behaves the same as `always`, except that the packed-refs file is opened and closed before its file attributes are considered. An open/close of the packed-refs file is known to refresh its file attributes, at least on some NFS clients. If `unset`, JGit will use the behavior described in `trustFolderStat`. | | `core.worktree` | Root directory of the working tree if it is not the parent directory of the `.git` directory | ✅ | The path to the root of the working tree. | ## __gc__ options @@ -1,23 +1,6 @@ workspace(name = "jgit") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -http_archive( - name = "bazel_skylib", - sha256 = "2ea8a5ed2b448baf4a6855d3ce049c4c452a6470b1efd1504fdb7c1c134d220a", - strip_prefix = "bazel-skylib-0.8.0", - urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.8.0.tar.gz"], -) - -# Check Bazel version when invoked by Bazel directly -load("//tools:bazelisk_version.bzl", "bazelisk_version") - -bazelisk_version(name = "bazelisk_version") - -load("@bazelisk_version//:check.bzl", "check_bazel_version") - -check_bazel_version() - load("//tools:bazlets.bzl", "load_bazlets") load_bazlets(commit = "f30a992da9fc855dce819875afb59f9dd6f860cd") @@ -28,32 +11,18 @@ load( ) http_archive( - name = "openjdk15_linux_archive", - build_file_content = """ -java_runtime(name = 'runtime', srcs = glob(['**']), visibility = ['//visibility:public']) -exports_files(["WORKSPACE"], visibility = ["//visibility:public"]) -""", - sha256 = "0a38f1138c15a4f243b75eb82f8ef40855afcc402e3c2a6de97ce8235011b1ad", - strip_prefix = "zulu15.27.17-ca-jdk15.0.0-linux_x64", + name = "rbe_jdk11", + sha256 = "766796de71916118e528b9f4334c29c9c9b4e926227bf3264dee555e6a4306c8", + strip_prefix = "rbe_autoconfig-2.0.0", urls = [ - "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-linux_x64.tar.gz", - "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-linux_x64.tar.gz", + "https://gerrit-bazel.storage.googleapis.com/rbe_autoconfig/v2.0.0.tar.gz", + "https://github.com/davido/rbe_autoconfig/archive/v2.0.0.tar.gz", ], ) -http_archive( - name = "openjdk15_darwin_archive", - build_file_content = """ -java_runtime(name = 'runtime', srcs = glob(['**']), visibility = ['//visibility:public']) -exports_files(["WORKSPACE"], visibility = ["//visibility:public"]) -""", - sha256 = "f80b2e0512d9d8a92be24497334c974bfecc8c898fc215ce0e76594f00437482", - strip_prefix = "zulu15.27.17-ca-jdk15.0.0-macosx_x64", - urls = [ - "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-macosx_x64.tar.gz", - "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-macosx_x64.tar.gz", - ], -) +register_toolchains("//tools:error_prone_warnings_toolchain_java11_definition") + +register_toolchains("//tools:error_prone_warnings_toolchain_java17_definition") JMH_VERS = "1.32" @@ -123,14 +92,14 @@ maven_jar( maven_jar( name = "sshd-osgi", - artifact = "org.apache.sshd:sshd-osgi:2.7.0", - sha1 = "a101aad0f79ad424498098f7e91c39d3d92177c1", + artifact = "org.apache.sshd:sshd-osgi:2.8.0", + sha1 = "b2a59b73c045f40d5722b9160d4f909a646d86c9", ) maven_jar( name = "sshd-sftp", - artifact = "org.apache.sshd:sshd-sftp:2.7.0", - sha1 = "0c9eff7145e20b338c1dd6aca36ba93ed7c0147c", + artifact = "org.apache.sshd:sshd-sftp:2.8.0", + sha1 = "d3cd9bc8d335b3ed1a86d2965deb4d202de27442", ) maven_jar( @@ -239,8 +208,8 @@ maven_jar( maven_jar( name = "gson", - artifact = "com.google.code.gson:gson:2.8.8", - sha1 = "431fc3cbc0ff81abdbfde070062741089c3ba874", + artifact = "com.google.code.gson:gson:2.8.9", + sha1 = "8a432c1d6825781e21a02db2e2c33c5fde2833b9", ) JETTY_VER = "10.0.6" @@ -294,32 +263,32 @@ maven_jar( src_sha1 = "f35f5525a5d30dc1237b85457d758d578e3ce8d0", ) -BOUNCYCASTLE_VER = "1.69" +BOUNCYCASTLE_VER = "1.70" maven_jar( name = "bcpg", artifact = "org.bouncycastle:bcpg-jdk15on:" + BOUNCYCASTLE_VER, - sha1 = "d99a08c3f651b26e8eb668e941b0bbd2c09ece08", - src_sha1 = "de1fc261b44a8eb60583413a31ffc98ce3dce38b", + sha1 = "062f72ec06f31a6c31a3f3355fce0384b21126d7", + src_sha1 = "9dee73ad926752ee3b421a7dc4531287166ccf36", ) maven_jar( name = "bcprov", artifact = "org.bouncycastle:bcprov-jdk15on:" + BOUNCYCASTLE_VER, - sha1 = "91e1628251cf3ca90093ce9d0fe67e5b7dab3850", - src_sha1 = "67dc6476845f6b29cb520b5df61db65ae56718e4", + sha1 = "4636a0d01f74acaf28082fb62b317f1080118371", + src_sha1 = "6245e15dd47e5fc33cff275df61662e0a8e5958f", ) maven_jar( name = "bcutil", artifact = "org.bouncycastle:bcutil-jdk15on:" + BOUNCYCASTLE_VER, - sha1 = "c3edf93d346e97f64f041e448e7455c39c7eff64", - src_sha1 = "deeb3fbbf373e05e2a20941f9a8ce90e9aeab3d2", + sha1 = "54280e7195a7430d7911ded93fc01e07300b9526", + src_sha1 = "4af4a6c92b8ea07885b27d8536b81b855497f4eb", ) maven_jar( name = "bcpkix", artifact = "org.bouncycastle:bcpkix-jdk15on:" + BOUNCYCASTLE_VER, - sha1 = "45c36fb72fafb0b688c6af795e6cc803f6f79ee5", - src_sha1 = "8bc5214401459bd91eea816b516079da38374e90", + sha1 = "f81e5af49571a9d5a109a88f239a73ce87055417", + src_sha1 = "42f9de53a91b20bc06e88482c57fd97e5a84250d", ) @@ -97,7 +97,7 @@ java_library( java_library( name = "jna", visibility = [ - "//org.eclipse.jgit.ssh.apache.agent:__pkg__", + "//org.eclipse.jgit.ssh.apache.agent:__pkg__", ], exports = ["@jna//jar"], ) @@ -105,7 +105,7 @@ java_library( java_library( name = "jna-platform", visibility = [ - "//org.eclipse.jgit.ssh.apache.agent:__pkg__", + "//org.eclipse.jgit.ssh.apache.agent:__pkg__", ], exports = ["@jna-platform//jar"], ) diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF index 94336588d6..09975705df 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.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.ant.tasks;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)", + 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.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 6ad64155e8..ba473eff29 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 10bfbd2eff..6003a31e86 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.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.storage.file;version="[6.0.1,6.1.0)" + org.eclipse.jgit.storage.file;version="[6.1.1,6.2.0)" Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.jgit.ant;version="6.0.1", - org.eclipse.jgit.ant.tasks;version="6.0.1"; +Export-Package: org.eclipse.jgit.ant;version="6.1.1", + org.eclipse.jgit.ant.tasks;version="6.1.1"; 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 c3ebfb3f48..d0dc5c8dd1 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ant;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ant;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml index 4448f2e148..a7f258be53 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 62fe5a5d81..3fc8df1229 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.0.1.qualifier +Bundle-Version: 6.1.1.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.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.nls;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.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.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.0.1"; +Export-Package: org.eclipse.jgit.archive;version="6.1.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.api, org.apache.commons.compress.archivers, org.osgi.framework", - org.eclipse.jgit.archive.internal;version="6.0.1";x-internal:=true + org.eclipse.jgit.archive.internal;version="6.1.1";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 deab2fab99..8c7fd07200 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.archive;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.archive;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml index 45f11da6a2..12f3e685d8 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 55c80c6567..ac1d6bd14d 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> <artifactId>org.eclipse.jgit.benchmarks</artifactId> <packaging>jar</packaging> diff --git a/org.eclipse.jgit.coverage/pom.xml b/org.eclipse.jgit.coverage/pom.xml index 63ead27a8f..abc37ec69f 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.0.1-SNAPSHOT</version> + <version>6.1.1-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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ant</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.archive</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.http.apache</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.http.server</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.lfs</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.lfs.server</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.pgm</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ui</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ssh.apache</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.test</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ant.test</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.http.test</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.pgm.test</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.lfs.test</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.lfs.server.test</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </dependency> </dependencies> diff --git a/org.eclipse.jgit.gpg.bc.test/BUILD b/org.eclipse.jgit.gpg.bc.test/BUILD index 35b125f21e..9e5813cb39 100644 --- a/org.eclipse.jgit.gpg.bc.test/BUILD +++ b/org.eclipse.jgit.gpg.bc.test/BUILD @@ -10,8 +10,8 @@ load( junit_tests( name = "bc", srcs = glob(["tst/**/*.java"]), - resource_jars = [":tst_rsrc"], tags = ["bc"], + runtime_deps = [":tst_rsrc"], deps = [ "//lib:bcpg", "//lib:bcprov", 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 d703432dc1..bb9b393f87 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.0.1.qualifier +Bundle-Version: 6.1.1.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.0.1,6.1.0)", - org.eclipse.jgit.gpg.bc.internal.keys;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util.sha1;version="[6.0.1,6.1.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.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 ad955b0b6e..9568c6884d 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 74e3305db2..534568dd80 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.0.1,6.1.0)" +Fragment-Host: org.eclipse.jgit;bundle-version="[6.1.1,6.2.0)" Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin -Bundle-Version: 6.0.1.qualifier +Bundle-Version: 6.1.1.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.0.1,6.1.0)", - org.eclipse.jgit.api.errors;version="[6.0.1,6.1.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.slf4j;version="[1.7.0,2.0.0)" -Export-Package: org.eclipse.jgit.gpg.bc;version="6.0.1", - org.eclipse.jgit.gpg.bc.internal;version="6.0.1";x-friends:="org.eclipse.jgit.gpg.bc.test", - org.eclipse.jgit.gpg.bc.internal.keys;version="6.0.1";x-friends:="org.eclipse.jgit.gpg.bc.test" +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" 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 de4962b2e8..20e2d01de1 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.gpg.bc/pom.xml b/org.eclipse.jgit.gpg.bc/pom.xml index 4bf82c64ed..138f4e8a4b 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 c51a87eadf..afcaa78d57 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.0.1.qualifier +Bundle-Version: 6.1.1.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.0.1,6.1.0)", - org.eclipse.jgit.nls;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.http;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)" -Export-Package: org.eclipse.jgit.transport.http.apache;version="6.0.1"; + 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"; 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 9d4f5f2ea8..7b8e1497eb 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml index 6e5f26028f..bd35589f54 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.apache</artifactId> diff --git a/org.eclipse.jgit.http.server/.settings/.api_filters b/org.eclipse.jgit.http.server/.settings/.api_filters new file mode 100644 index 0000000000..2c32c9864b --- /dev/null +++ b/org.eclipse.jgit.http.server/.settings/.api_filters @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<component id="org.eclipse.jgit.http.server" version="2"> + <resource path="src/org/eclipse/jgit/http/server/UploadPackErrorHandler.java" type="org.eclipse.jgit.http.server.UploadPackErrorHandler"> + <filter id="1210056707"> + <message_arguments> + <message_argument value="6.1.1"/> + <message_argument value="statusCodeForThrowable(Throwable)"/> + </message_arguments> + </filter> + </resource> +</component> diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF index 510c4397de..4bf66a2e97 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.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.jgit.http.server;version="6.0.1", - org.eclipse.jgit.http.server.glue;version="6.0.1"; +Export-Package: org.eclipse.jgit.http.server;version="6.1.1", + org.eclipse.jgit.http.server.glue;version="6.1.1"; uses:="javax.servlet,javax.servlet.http", - org.eclipse.jgit.http.server.resolver;version="6.0.1"; + org.eclipse.jgit.http.server.resolver;version="6.1.1"; 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.0.1,6.1.0)", - org.eclipse.jgit.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.dfs;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.transport.parser;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.nls;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.resolver;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.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)" 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 7453cb8bd8..2b47055232 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml index b01c7910d2..6a788e2323 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.server</artifactId> diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java index 012f9a3dc0..f1155dcf57 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java @@ -158,11 +158,11 @@ public class GitSmartHttpTools { } if (isInfoRefs(req)) { - sendInfoRefsError(req, res, textForGit); + sendInfoRefsError(req, res, textForGit, httpStatus); } else if (isUploadPack(req)) { - sendUploadPackError(req, res, textForGit); + sendUploadPackError(req, res, textForGit, httpStatus); } else if (isReceivePack(req)) { - sendReceivePackError(req, res, textForGit); + sendReceivePackError(req, res, textForGit, httpStatus); } else { if (httpStatus < 400) ServletUtils.consumeRequestBody(req); @@ -171,29 +171,32 @@ public class GitSmartHttpTools { } private static void sendInfoRefsError(HttpServletRequest req, - HttpServletResponse res, String textForGit) throws IOException { + HttpServletResponse res, String textForGit, int httpStatus) + throws IOException { ByteArrayOutputStream buf = new ByteArrayOutputStream(128); PacketLineOut pck = new PacketLineOut(buf); String svc = req.getParameter("service"); pck.writeString("# service=" + svc + "\n"); pck.end(); pck.writeString("ERR " + textForGit); - send(req, res, infoRefsResultType(svc), buf.toByteArray()); + send(req, res, infoRefsResultType(svc), buf.toByteArray(), httpStatus); } private static void sendUploadPackError(HttpServletRequest req, - HttpServletResponse res, String textForGit) throws IOException { + HttpServletResponse res, String textForGit, int httpStatus) + throws IOException { // Do not use sideband. Sideband is acceptable only while packfile is // being sent. Other places, like acknowledgement section, do not // support sideband. Use an error packet. ByteArrayOutputStream buf = new ByteArrayOutputStream(128); PacketLineOut pckOut = new PacketLineOut(buf); writePacket(pckOut, textForGit); - send(req, res, UPLOAD_PACK_RESULT_TYPE, buf.toByteArray()); + send(req, res, UPLOAD_PACK_RESULT_TYPE, buf.toByteArray(), httpStatus); } private static void sendReceivePackError(HttpServletRequest req, - HttpServletResponse res, String textForGit) throws IOException { + HttpServletResponse res, String textForGit, int httpStatus) + throws IOException { ByteArrayOutputStream buf = new ByteArrayOutputStream(128); PacketLineOut pckOut = new PacketLineOut(buf); @@ -212,7 +215,7 @@ public class GitSmartHttpTools { writeSideBand(buf, textForGit); else writePacket(pckOut, textForGit); - send(req, res, RECEIVE_PACK_RESULT_TYPE, buf.toByteArray()); + send(req, res, RECEIVE_PACK_RESULT_TYPE, buf.toByteArray(), httpStatus); } private static boolean isReceivePackSideBand(HttpServletRequest req) { @@ -246,9 +249,9 @@ public class GitSmartHttpTools { } private static void send(HttpServletRequest req, HttpServletResponse res, - String type, byte[] buf) throws IOException { + String type, byte[] buf, int httpStatus) throws IOException { ServletUtils.consumeRequestBody(req); - res.setStatus(HttpServletResponse.SC_OK); + res.setStatus(httpStatus); res.setContentType(type); res.setContentLength(buf.length); try (OutputStream os = res.getOutputStream()) { diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackErrorHandler.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackErrorHandler.java index 03be0873b0..2aadbbc984 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackErrorHandler.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackErrorHandler.java @@ -9,13 +9,19 @@ */ package org.eclipse.jgit.http.server; +import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; +import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; +import static javax.servlet.http.HttpServletResponse.SC_OK; + import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.transport.ServiceMayNotContinueException; import org.eclipse.jgit.transport.UploadPack; +import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; /** * Handle git-upload-pack errors. @@ -35,6 +41,24 @@ import org.eclipse.jgit.transport.UploadPack; */ public interface UploadPackErrorHandler { /** + * Maps a thrown git related Exception to an appropriate HTTP status code. + * + * @param error + * The thrown Exception. + * @return the HTTP status code as an int + * @since 6.1.1 + */ + public static int statusCodeForThrowable(Throwable error) { + if (error instanceof ServiceNotEnabledException) { + return SC_FORBIDDEN; + } + if (error instanceof PackProtocolException) { + // Internal git errors are not errors from an HTTP standpoint. + return SC_OK; + } + return SC_INTERNAL_SERVER_ERROR; + } + /** * @param req * The HTTP request * @param rsp diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java index c4f845e52b..b0a07f1d5d 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java @@ -11,7 +11,6 @@ package org.eclipse.jgit.http.server; import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; -import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK; @@ -22,6 +21,7 @@ import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER; import static org.eclipse.jgit.http.server.ServletUtils.consumeRequestBody; import static org.eclipse.jgit.http.server.ServletUtils.getInputStream; import static org.eclipse.jgit.http.server.ServletUtils.getRepository; +import static org.eclipse.jgit.http.server.UploadPackErrorHandler.statusCodeForThrowable; import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT; import java.io.IOException; @@ -169,6 +169,7 @@ class UploadPackServlet extends HttpServlet { UploadPackRunnable r = () -> { UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER); + // to be explicitly closed by caller @SuppressWarnings("resource") SmartOutputStream out = new SmartOutputStream(req, rsp, false) { @Override @@ -217,9 +218,12 @@ class UploadPackServlet extends HttpServlet { log(up.getRepository(), e); if (!rsp.isCommitted()) { rsp.reset(); - String msg = e instanceof PackProtocolException ? e.getMessage() - : null; - sendError(req, rsp, SC_INTERNAL_SERVER_ERROR, msg); + String msg = null; + if (e instanceof PackProtocolException + || e instanceof ServiceNotEnabledException) { + msg = e.getMessage(); + } + sendError(req, rsp, statusCodeForThrowable(e), msg); } } } diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF index 3a6517c685..4150bdb403 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.0.1.qualifier +Bundle-Version: 6.1.1.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.0.1,6.1.0)", - org.eclipse.jgit.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.http.server;version="[6.0.1,6.1.0)", - org.eclipse.jgit.http.server.glue;version="[6.0.1,6.1.0)", - org.eclipse.jgit.http.server.resolver;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.dfs;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.reftable;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit.http;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.nls;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.http;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.http.apache;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.resolver;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.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.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 7694c441d9..336f43bfff 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.test</artifactId> diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java index db9f2ae3d7..20de2567c1 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java @@ -97,6 +97,7 @@ public class HookMessageTest extends AllFactoriesHttpTestCase { server.setUp(); remoteRepository = src.getRepository(); + addRepoToClose(remoteRepository); remoteURI = toURIish(app, srcName); StoredConfig cfg = remoteRepository.getConfig(); diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java index df093c185b..3438c52c8d 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java @@ -23,6 +23,7 @@ import java.io.File; import java.io.OutputStream; import java.net.URI; import java.net.URL; +import java.text.MessageFormat; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -308,7 +309,10 @@ public class HttpClientTests extends AllFactoriesHttpTestCase { fail("connection opened even though service disabled"); } catch (TransportException err) { String exp = smartAuthNoneURI + ": " - + JGitText.get().serviceNotEnabledNoName; + + MessageFormat.format( + JGitText.get().serviceNotPermitted, + smartAuthNoneURI.toString() + "/", + "git-upload-pack"); assertEquals(exp, err.getMessage()); } } diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java index 9ffa19efef..000eecdccf 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java @@ -90,6 +90,7 @@ public class MeasurePackSizeTest extends AllFactoriesHttpTestCase { server.setUp(); remoteRepository = src.getRepository(); + addRepoToClose(remoteRepository); remoteURI = toURIish(app, srcName); StoredConfig cfg = remoteRepository.getConfig(); diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java index 887e970a0c..b9b10b45d0 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java @@ -57,7 +57,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.TransportConfigCallback; -import org.eclipse.jgit.errors.RemoteRepositoryException; +import org.eclipse.jgit.errors.NoRemoteRepositoryException; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.errors.UnsupportedCredentialItem; import org.eclipse.jgit.http.server.GitServlet; @@ -496,8 +496,9 @@ public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase { try { t.openFetch(); fail("fetch connection opened"); - } catch (RemoteRepositoryException notFound) { - assertEquals(uri + ": Git repository not found", + } catch (NoRemoteRepositoryException notFound) { + assertEquals(uri + ": " + uri + + "/info/refs?service=git-upload-pack not found: Not Found", notFound.getMessage()); } } @@ -510,7 +511,7 @@ public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase { assertEquals(join(uri, "info/refs"), info.getPath()); assertEquals(1, info.getParameters().size()); assertEquals("git-upload-pack", info.getParameter("service")); - assertEquals(200, info.getStatus()); + assertEquals(404, info.getStatus()); assertEquals("application/x-git-upload-pack-advertisement", info.getResponseHeader(HDR_CONTENT_TYPE)); } @@ -538,6 +539,7 @@ public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase { assertTrue(e.getMessage().contains( "want " + unreachableCommit.name() + " not valid")); } + assertLastRequestStatusCode(200); } @Test @@ -560,6 +562,7 @@ public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase { assertTrue( e.getMessage().contains("want " + A.name() + " not valid")); } + assertLastRequestStatusCode(200); } @Test @@ -916,6 +919,7 @@ public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase { } catch (TransportException e) { assertTrue(e.getMessage().contains("301")); } + assertLastRequestStatusCode(301); } @Test @@ -934,6 +938,7 @@ public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase { assertTrue( e.getMessage().contains("http.followRedirects is false")); } + assertLastRequestStatusCode(301); } private void assertFetchRequests(List<AccessEvent> requests, int index) { @@ -1607,6 +1612,7 @@ public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase { assertTrue(err.getMessage() .contains("want " + id.name() + " not valid")); } + assertLastRequestStatusCode(200); } @Test @@ -1650,7 +1656,7 @@ public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase { fail("Successfully served ref with value " + c.getRef(master)); } catch (TransportException err) { assertTrue("Unexpected exception message " + err.getMessage(), - err.getMessage().contains("Internal server error")); + err.getMessage().contains("Server Error")); } } finally { noRefServer.tearDown(); @@ -1821,6 +1827,11 @@ public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase { .getResponseHeader(HDR_CONTENT_TYPE)); } + private void assertLastRequestStatusCode(int statusCode) { + List<AccessEvent> requests = getRequests(); + assertEquals(statusCode, requests.get(requests.size() - 1).getStatus()); + } + private void enableReceivePack() throws IOException { final StoredConfig cfg = remoteRepository.getConfig(); cfg.setBoolean("http", null, "receivepack", true); diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF index c21f835d2a..d035072b5b 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.0.1.qualifier +Bundle-Version: 6.1.1.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.0.1,6.1.0)", - org.eclipse.jgit.http.server;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.resolver;version="[6.0.1,6.1.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.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.0.1"; +Export-Package: org.eclipse.jgit.junit.http;version="6.1.1"; 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 98fde06580..19142c51fd 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml index b66991f9ce..d883e03879 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.junit.http</artifactId> diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java index 3e7c84f667..877b918695 100644 --- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java +++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Set; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jgit.internal.storage.file.FileRepository; import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.AnyObjectId; @@ -80,7 +81,9 @@ public abstract class HttpTestCase extends LocalDiskRepositoryTestCase { */ protected TestRepository<Repository> createTestRepository() throws IOException { - return new TestRepository<>(createBareRepository()); + final FileRepository repository = createBareRepository(); + addRepoToClose(repository); + return new TestRepository<>(repository); } /** diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF index 8d629eda6a..60858d8c01 100644 --- a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF @@ -3,46 +3,46 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.junit.ssh Bundle-SymbolicName: org.eclipse.jgit.junit.ssh -Bundle-Version: 6.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 -Import-Package: org.apache.sshd.common;version="[2.7.0,2.8.0)", - org.apache.sshd.common.config.keys;version="[2.7.0,2.8.0)", - org.apache.sshd.common.file.virtualfs;version="[2.7.0,2.8.0)", - org.apache.sshd.common.helpers;version="[2.7.0,2.8.0)", - org.apache.sshd.common.io;version="[2.7.0,2.8.0)", - org.apache.sshd.common.kex;version="[2.7.0,2.8.0)", - org.apache.sshd.common.keyprovider;version="[2.7.0,2.8.0)", - org.apache.sshd.common.session;version="[2.7.0,2.8.0)", - org.apache.sshd.common.signature;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.buffer;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.logging;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.security;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.threads;version="[2.7.0,2.8.0)", - org.apache.sshd.core;version="[2.7.0,2.8.0)", - org.apache.sshd.server;version="[2.7.0,2.8.0)", - org.apache.sshd.server.auth;version="[2.7.0,2.8.0)", - org.apache.sshd.server.auth.gss;version="[2.7.0,2.8.0)", - org.apache.sshd.server.auth.keyboard;version="[2.7.0,2.8.0)", - org.apache.sshd.server.auth.password;version="[2.7.0,2.8.0)", - org.apache.sshd.server.command;version="[2.7.0,2.8.0)", - org.apache.sshd.server.session;version="[2.7.0,2.8.0)", - org.apache.sshd.server.shell;version="[2.7.0,2.8.0)", - org.apache.sshd.server.subsystem;version="[2.7.0,2.8.0)", - org.apache.sshd.sftp;version="[2.7.0,2.8.0)", - org.apache.sshd.sftp.server;version="[2.7.0,2.8.0)", - org.eclipse.jgit.annotations;version="[6.0.1,6.1.0)", - org.eclipse.jgit.api;version="[6.0.1,6.1.0)", - org.eclipse.jgit.api.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)", +Import-Package: org.apache.sshd.common;version="[2.8.0,2.9.0)", + org.apache.sshd.common.config.keys;version="[2.8.0,2.9.0)", + org.apache.sshd.common.file.virtualfs;version="[2.8.0,2.9.0)", + org.apache.sshd.common.helpers;version="[2.8.0,2.9.0)", + org.apache.sshd.common.io;version="[2.8.0,2.9.0)", + org.apache.sshd.common.kex;version="[2.8.0,2.9.0)", + org.apache.sshd.common.keyprovider;version="[2.8.0,2.9.0)", + org.apache.sshd.common.session;version="[2.8.0,2.9.0)", + org.apache.sshd.common.signature;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.buffer;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.logging;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.security;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.threads;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.auth;version="[2.8.0,2.9.0)", + org.apache.sshd.server.auth.gss;version="[2.8.0,2.9.0)", + org.apache.sshd.server.auth.keyboard;version="[2.8.0,2.9.0)", + org.apache.sshd.server.auth.password;version="[2.8.0,2.9.0)", + org.apache.sshd.server.command;version="[2.8.0,2.9.0)", + org.apache.sshd.server.session;version="[2.8.0,2.9.0)", + org.apache.sshd.server.shell;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.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.0.1" +Export-Package: org.eclipse.jgit.junit.ssh;version="6.1.1" 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 7132b5b828..41246bcee9 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml index 76bdca16ae..07d0218bf4 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.junit.ssh</artifactId> @@ -55,6 +55,16 @@ <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.junit/.settings/.api_filters b/org.eclipse.jgit.junit/.settings/.api_filters new file mode 100644 index 0000000000..a17a72f403 --- /dev/null +++ b/org.eclipse.jgit.junit/.settings/.api_filters @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<component id="org.eclipse.jgit.junit" version="2"> + <resource path="src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java" type="org.eclipse.jgit.junit.LocalDiskRepositoryTestCase"> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.junit.LocalDiskRepositoryTestCase"/> + <message_argument value="currentTest"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="6.0.1"/> + <message_argument value="currentTest"/> + </message_arguments> + </filter> + </resource> +</component> diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF index 1f021da61b..7a3b200e17 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.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 -Import-Package: org.eclipse.jgit.annotations;version="[6.0.1,6.1.0)", - org.eclipse.jgit.api;version="[6.0.1,6.1.0)", - org.eclipse.jgit.api.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.dircache;version="[6.0.1,6.1.0)", - org.eclipse.jgit.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.pack;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.merge;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="6.0.1", - org.eclipse.jgit.treewalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.treewalk.filter;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util.io;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util.time;version="[6.0.1,6.1.0)", +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)", 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.0.1"; +Export-Package: org.eclipse.jgit.junit;version="6.1.1"; 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.0.1"; org.junit.runners.model, org.junit.runner, org.eclipse.jgit.util.time", - org.eclipse.jgit.junit.time;version="6.0.1";uses:="org.eclipse.jgit.util.time" + org.eclipse.jgit.junit.time;version="6.1.1";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 156e59a863..25876c7fc7 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.junit;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.junit;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml index ef2b946dac..2c260e2475 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.junit</artifactId> diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java index 494d2f3bae..59662cec95 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java @@ -45,6 +45,8 @@ import org.eclipse.jgit.util.FileUtils; import org.eclipse.jgit.util.SystemReader; import org.junit.After; import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.TestName; /** * JUnit TestCase with specialized support for temporary local repository. @@ -84,18 +86,35 @@ public abstract class LocalDiskRepositoryTestCase { private File tmp; /** + * The current test name. + * + * @since 6.0.1 + */ + @Rule + public TestName currentTest = new TestName(); + + private String getTestName() { + String name = currentTest.getMethodName(); + name = name.replaceAll("[^a-zA-Z0-9]", "_"); + name = name.replaceAll("__+", "_"); + if (name.startsWith("_")) { + name = name.substring(1); + } + return name; + } + + /** * Setup test * * @throws Exception */ @Before public void setUp() throws Exception { - tmp = File.createTempFile("jgit_test_", "_tmp"); + tmp = File.createTempFile("jgit_" + getTestName() + '_', "_tmp"); CleanupThread.deleteOnShutdown(tmp); if (!tmp.delete() || !tmp.mkdir()) { throw new IOException("Cannot create " + tmp); } - mockSystemReader = new MockSystemReader(); SystemReader.setInstance(mockSystemReader); 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 b2a82c2215..cbb8135d77 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.0.1.qualifier +Bundle-Version: 6.1.1.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.0.1,6.1.0)", - org.eclipse.jgit.api.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit.http;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.server;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.server.fs;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.test;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.treewalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.treewalk.filter;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.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.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 ef8751e9f9..1bab620881 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs.server.test</artifactId> diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java index 70c0463e11..06708b334a 100644 --- a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java +++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java @@ -11,6 +11,7 @@ package org.eclipse.jgit.lfs.server.fs; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.io.InputStream; import java.nio.file.Files; @@ -29,6 +30,7 @@ import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathFilter; @@ -128,4 +130,18 @@ public class PushTest extends LfsServerTest { server.getRequests().toString()); } + @Test + public void testDeleteBranch() throws Exception { + String branch = "new-branch"; + git.branchCreate().setName(branch).call(); + + String destRef = Constants.R_HEADS + branch; + git.push().setRefSpecs(new RefSpec().setSource(branch).setDestination(destRef)).call(); + + // Should not fail on push. + git.branchDelete().setBranchNames(branch).setForce(true).call(); + git.push().setRefSpecs(new RefSpec().setSource(null).setDestination(destRef)).call(); + + assertTrue(server.getRequests().isEmpty()); + } } diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF index 3a4448b9c1..cd8bde3a8e 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.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.jgit.lfs.server;version="6.0.1"; +Export-Package: org.eclipse.jgit.lfs.server;version="6.1.1"; uses:="javax.servlet.http, org.eclipse.jgit.lfs.lib", - org.eclipse.jgit.lfs.server.fs;version="6.0.1"; + org.eclipse.jgit.lfs.server.fs;version="6.1.1"; uses:="javax.servlet, javax.servlet.http, org.eclipse.jgit.lfs.server, org.eclipse.jgit.lfs.lib", - org.eclipse.jgit.lfs.server.internal;version="6.0.1";x-internal:=true, - org.eclipse.jgit.lfs.server.s3;version="6.0.1"; + org.eclipse.jgit.lfs.server.internal;version="6.1.1";x-internal:=true, + org.eclipse.jgit.lfs.server.s3;version="6.1.1"; 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.0.1,6.1.0)", - org.eclipse.jgit.internal;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.internal;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.nls;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.http;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.http.apache;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.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.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 5011a223e1..5a5b23add3 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml index c2d6596af3..ae27153d71 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 b870794b84..e3c0af81de 100644 --- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF @@ -3,24 +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.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-11 -Import-Package: org.eclipse.jgit.api;version="[6.0.1,6.1.0)", - org.eclipse.jgit.attributes;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.dfs;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.treewalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.treewalk.filter;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)", +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)", 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.0.1";x-friends:="org.eclipse.jgit.lfs.server.test" +Export-Package: org.eclipse.jgit.lfs.test;version="6.1.1";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 a88ca94860..1d782e6fd3 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 new file mode 100644 index 0000000000..98a0712e47 --- /dev/null +++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/LfsConfigGitTest.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2022, Matthias Fromme <mfromme@dspace.de> + * + * 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.lfs; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.ResetCommand.ResetType; +import org.eclipse.jgit.attributes.FilterCommand; +import org.eclipse.jgit.attributes.FilterCommandRegistry; +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lfs.internal.LfsConnectionFactory; +import org.eclipse.jgit.lfs.lib.Constants; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.transport.http.HttpConnection; +import org.eclipse.jgit.util.HttpSupport; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Test if the lfs config is used in the correct way during checkout. + * + * Two lfs-files are created, one that comes before .gitattributes and + * .lfsconfig in git order (".aaa.txt") and one that comes after ("zzz.txt"). + * + * During checkout/reset it is tested if the correct version of the lfs config + * is used. + * + * TODO: The current behavior seems a little bit strange/unintuitive. Some files + * are checked out before and some after the config files. This leads to the + * behavior, that during a single command the config changes. Since this seems + * to be the same way in native git, the behavior is accepted for now. + * + */ +public class LfsConfigGitTest extends RepositoryTestCase { + + private static final String SMUDGE_NAME = org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX + + Constants.ATTR_FILTER_DRIVER_PREFIX + + org.eclipse.jgit.lib.Constants.ATTR_FILTER_TYPE_SMUDGE; + + private static final String LFS_SERVER_URI1 = "https://lfs.server1/test/uri"; + + private static final String EXPECTED_SERVER_URL1 = LFS_SERVER_URI1 + + Protocol.OBJECTS_LFS_ENDPOINT; + + private static final String LFS_SERVER_URI2 = "https://lfs.server2/test/uri"; + + private static final String EXPECTED_SERVER_URL2 = LFS_SERVER_URI2 + + Protocol.OBJECTS_LFS_ENDPOINT; + + private static final String LFS_SERVER_URI3 = "https://lfs.server3/test/uri"; + + private static final String EXPECTED_SERVER_URL3 = LFS_SERVER_URI3 + + Protocol.OBJECTS_LFS_ENDPOINT; + + private static final String FAKE_LFS_POINTER1 = "version https://git-lfs.github.com/spec/v1\n" + + "oid sha256:6ce9fab52ee9a6c4c097def4e049c6acdeba44c99d26e83ba80adec1473c9b2d\n" + + "size 253952\n"; + + private static final String FAKE_LFS_POINTER2 = "version https://git-lfs.github.com/spec/v1\n" + + "oid sha256:a4b711cd989863ae2038758a62672138347abbbae4076a7ad3a545fda7d08f82\n" + + "size 67072\n"; + + private static List<String> checkoutURLs = new ArrayList<>(); + + static class SmudgeFilterMock extends FilterCommand { + public SmudgeFilterMock(Repository db, InputStream in, + OutputStream out) throws IOException { + super(in, out); + HttpConnection lfsServerConn = LfsConnectionFactory.getLfsConnection(db, + HttpSupport.METHOD_POST, Protocol.OPERATION_DOWNLOAD); + checkoutURLs.add(lfsServerConn.getURL().toString()); + } + + @Override + public int run() throws IOException { + // Stupid no impl + in.transferTo(out); + return -1; + } + } + + @BeforeClass + public static void installLfs() { + FilterCommandRegistry.register(SMUDGE_NAME, SmudgeFilterMock::new); + } + + @AfterClass + public static void removeLfs() { + FilterCommandRegistry.unregister(SMUDGE_NAME); + } + + private Git git; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + git = new Git(db); + // commit something + writeTrashFile("Test.txt", "Hello world"); + git.add().addFilepattern("Test.txt").call(); + git.commit().setMessage("Initial commit").call(); + // prepare the config for LFS + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "lfs", "smudge", SMUDGE_NAME); + config.setString(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_AUTOCRLF, "false"); + config.save(); + + fileBefore = null; + fileAfter = null; + configFile = null; + gitAttributesFile = null; + } + + File fileBefore; + + File fileAfter; + + File configFile; + + 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); + git.add().addFilepattern(fileNameBefore).call(); + + // File to be checked out after lfs config + String fileNameAfter = "zzz.txt"; + fileAfter = writeTrashFile(fileNameAfter, lfsPointer); + git.add().addFilepattern(fileNameAfter).call(); + + git.commit().setMessage("Commit LFS Pointer files").call(); + } + + + private String addLfsConfigFiles(String lfsServerUrl) throws Exception { + // Add config files to the repo + String lfsConfig1 = createLfsConfig(lfsServerUrl); + git.add().addFilepattern(Constants.DOT_LFS_CONFIG).call(); + // Modify gitattributes on second call, to force checkout too. + if (gitAttributesFile == null) { + gitAttributesFile = writeTrashFile(".gitattributes", + "*.txt filter=lfs"); + } else { + gitAttributesFile = writeTrashFile(".gitattributes", + "*.txt filter=lfs\n"); + } + + git.add().addFilepattern(".gitattributes").call(); + git.commit().setMessage("Commit config files").call(); + return lfsConfig1; + } + + private String createLfsConfig(String lfsServerUrl) throws IOException { + String lfsConfig1 = "[lfs]\n url = " + lfsServerUrl; + configFile = writeTrashFile(Constants.DOT_LFS_CONFIG, lfsConfig1); + return lfsConfig1; + } + + @Test + public void checkoutLfsObjects_reset() throws Exception { + createLfsFiles(FAKE_LFS_POINTER1); + String lfsConfig1 = addLfsConfigFiles(LFS_SERVER_URI1); + + // Delete files to force action on reset + assertTrue(configFile.delete()); + assertTrue(fileBefore.delete()); + assertTrue(fileAfter.delete()); + + assertTrue(gitAttributesFile.delete()); + + // create config file with different url + createLfsConfig(LFS_SERVER_URI3); + + checkoutURLs.clear(); + git.reset().setMode(ResetType.HARD).call(); + + checkFile(configFile, lfsConfig1); + checkFile(fileBefore, FAKE_LFS_POINTER1); + checkFile(fileAfter, FAKE_LFS_POINTER1); + + assertEquals(2, checkoutURLs.size()); + // TODO: Should may be EXPECTED_SERVR_URL1 + assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(0)); + assertEquals(EXPECTED_SERVER_URL1, checkoutURLs.get(1)); + } + + @Test + public void checkoutLfsObjects_BranchSwitch() throws Exception { + // Create a new branch "URL1" and add config files + git.checkout().setCreateBranch(true).setName("URL1").call(); + + createLfsFiles(FAKE_LFS_POINTER1); + String lfsConfig1 = addLfsConfigFiles(LFS_SERVER_URI1); + + // Create a second new branch "URL2" and add config files + git.checkout().setCreateBranch(true).setName("URL2").call(); + + createLfsFiles(FAKE_LFS_POINTER2); + String lfsConfig2 = addLfsConfigFiles(LFS_SERVER_URI2); + + checkFile(configFile, lfsConfig2); + checkFile(fileBefore, FAKE_LFS_POINTER2); + checkFile(fileAfter, FAKE_LFS_POINTER2); + + checkoutURLs.clear(); + git.checkout().setName("URL1").call(); + + checkFile(configFile, lfsConfig1); + checkFile(fileBefore, FAKE_LFS_POINTER1); + checkFile(fileAfter, FAKE_LFS_POINTER1); + + assertEquals(2, checkoutURLs.size()); + // TODO: Should may be EXPECTED_SERVR_URL1 + assertEquals(EXPECTED_SERVER_URL2, checkoutURLs.get(0)); + assertEquals(EXPECTED_SERVER_URL1, checkoutURLs.get(1)); + + checkoutURLs.clear(); + git.checkout().setName("URL2").call(); + + checkFile(configFile, lfsConfig2); + checkFile(fileBefore, FAKE_LFS_POINTER2); + checkFile(fileAfter, FAKE_LFS_POINTER2); + + assertEquals(2, checkoutURLs.size()); + // TODO: Should may be EXPECTED_SERVR_URL2 + assertEquals(EXPECTED_SERVER_URL1, checkoutURLs.get(0)); + assertEquals(EXPECTED_SERVER_URL2, checkoutURLs.get(1)); + } + + @Test + public void checkoutLfsObjects_BranchSwitch_ModifiedLocal() + throws Exception { + + // Create a new branch "URL1" and add config files + git.checkout().setCreateBranch(true).setName("URL1").call(); + + createLfsFiles(FAKE_LFS_POINTER1); + addLfsConfigFiles(LFS_SERVER_URI1); + + // Create a second new branch "URL2" and add config files + git.checkout().setCreateBranch(true).setName("URL2").call(); + + createLfsFiles(FAKE_LFS_POINTER2); + addLfsConfigFiles(LFS_SERVER_URI1); + + // create config file with different url + assertTrue(configFile.delete()); + String lfsConfig3 = createLfsConfig(LFS_SERVER_URI3); + + checkFile(configFile, lfsConfig3); + checkFile(fileBefore, FAKE_LFS_POINTER2); + checkFile(fileAfter, FAKE_LFS_POINTER2); + + checkoutURLs.clear(); + git.checkout().setName("URL1").call(); + + checkFile(fileBefore, FAKE_LFS_POINTER1); + checkFile(fileAfter, FAKE_LFS_POINTER1); + checkFile(configFile, lfsConfig3); + + assertEquals(2, checkoutURLs.size()); + + assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(0)); + assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(1)); + + checkoutURLs.clear(); + git.checkout().setName("URL2").call(); + + checkFile(fileBefore, FAKE_LFS_POINTER2); + checkFile(fileAfter, FAKE_LFS_POINTER2); + checkFile(configFile, lfsConfig3); + + assertEquals(2, checkoutURLs.size()); + assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(0)); + assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(1)); + } +} diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/LfsGitTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/LfsGitTest.java index 8964310e41..3e83c8ef49 100644 --- a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/LfsGitTest.java +++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/LfsGitTest.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 @@ -68,6 +68,27 @@ public class LfsGitTest extends RepositoryTestCase { } @Test + public void testBranchSwitch() throws Exception { + git.branchCreate().setName("abranch").call(); + git.checkout().setName("abranch").call(); + File aFile = writeTrashFile("a.bin", "aaa"); + writeTrashFile(".gitattributes", "a.bin filter=lfs"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("acommit").call(); + git.checkout().setName("master").call(); + git.branchCreate().setName("bbranch").call(); + git.checkout().setName("bbranch").call(); + File bFile = writeTrashFile("b.bin", "bbb"); + writeTrashFile(".gitattributes", "b.bin filter=lfs"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("bcommit").call(); + git.checkout().setName("abranch").call(); + checkFile(aFile, "aaa"); + git.checkout().setName("bbranch").call(); + checkFile(bFile, "bbb"); + } + + @Test public void checkoutNonLfsPointer() throws Exception { String content = "size_t\nsome_function(void* ptr);\n"; File smallFile = writeTrashFile("Test.txt", content); diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/internal/LfsConnectionFactoryTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/internal/LfsConnectionFactoryTest.java new file mode 100644 index 0000000000..badcb7d7e5 --- /dev/null +++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/internal/LfsConnectionFactoryTest.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2022 Nail Samatov <sanail@yandex.ru> and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.lfs.internal; + +import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.RemoteAddCommand; +import org.eclipse.jgit.attributes.FilterCommandRegistry; +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lfs.CleanFilter; +import org.eclipse.jgit.lfs.Protocol; +import org.eclipse.jgit.lfs.SmudgeFilter; +import org.eclipse.jgit.lfs.errors.LfsConfigInvalidException; +import org.eclipse.jgit.lfs.lib.Constants; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.transport.http.HttpConnection; +import org.eclipse.jgit.util.HttpSupport; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class LfsConnectionFactoryTest extends RepositoryTestCase { + + private static final String SMUDGE_NAME = org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX + + Constants.ATTR_FILTER_DRIVER_PREFIX + + org.eclipse.jgit.lib.Constants.ATTR_FILTER_TYPE_SMUDGE; + + private static final String CLEAN_NAME = org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX + + Constants.ATTR_FILTER_DRIVER_PREFIX + + org.eclipse.jgit.lib.Constants.ATTR_FILTER_TYPE_CLEAN; + + private final static String LFS_SERVER_URL1 = "https://lfs.server1/test/uri"; + + private final static String LFS_SERVER_URL2 = "https://lfs.server2/test/uri"; + + private final static String ORIGIN_URL = "https://git.server/test/uri"; + + private Git git; + + @BeforeClass + public static void installLfs() { + FilterCommandRegistry.register(SMUDGE_NAME, SmudgeFilter.FACTORY); + FilterCommandRegistry.register(CLEAN_NAME, CleanFilter.FACTORY); + } + + @AfterClass + public static void removeLfs() { + FilterCommandRegistry.unregister(SMUDGE_NAME); + FilterCommandRegistry.unregister(CLEAN_NAME); + } + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + git = new Git(db); + + // Just to have a non empty repo + writeTrashFile("Test.txt", "Hello world from the LFS Factory Test"); + git.add().addFilepattern("Test.txt").call(); + git.commit().setMessage("Initial commit").call(); + } + + @Test + public void lfsUrlFromRemoteUrlWithDotGit() throws Exception { + addRemoteUrl("https://localhost/repo.git"); + checkLfsUrl("https://localhost/repo.git/info/lfs"); + } + + @Test + public void lfsUrlFromRemoteUrlWithoutDotGit() throws Exception { + addRemoteUrl("https://localhost/repo"); + checkLfsUrl("https://localhost/repo.git/info/lfs"); + } + + @Test + public void lfsUrlFromLocalConfig() throws Exception { + addRemoteUrl("https://localhost/repo"); + + StoredConfig cfg = ((Repository) db).getConfig(); + cfg.setString(ConfigConstants.CONFIG_SECTION_LFS, + null, + ConfigConstants.CONFIG_KEY_URL, + "https://localhost/repo/lfs"); + cfg.save(); + + checkLfsUrl("https://localhost/repo/lfs"); + } + + @Test + public void lfsUrlFromOriginConfig() throws Exception { + addRemoteUrl("https://localhost/repo"); + + StoredConfig cfg = ((Repository) db).getConfig(); + cfg.setString(ConfigConstants.CONFIG_SECTION_LFS, + org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME, + ConfigConstants.CONFIG_KEY_URL, + "https://localhost/repo/lfs"); + cfg.save(); + + checkLfsUrl("https://localhost/repo/lfs"); + } + + @Test + public void lfsUrlNotConfigured() throws Exception { + assertThrows(LfsConfigInvalidException.class, + () -> LfsConnectionFactory.getLfsConnection(db, + HttpSupport.METHOD_POST, Protocol.OPERATION_DOWNLOAD)); + } + + @Test + public void checkGetLfsConnection_lfsurl_lfsconfigFromWorkingDir() + throws Exception { + writeLfsConfig(); + checkLfsUrl(LFS_SERVER_URL1); + } + + @Test + public void checkGetLfsConnection_lfsurl_lfsconfigFromIndex() + throws Exception { + writeLfsConfig(); + git.add().addFilepattern(Constants.DOT_LFS_CONFIG).call(); + deleteTrashFile(Constants.DOT_LFS_CONFIG); + checkLfsUrl(LFS_SERVER_URL1); + } + + @Test + public void checkGetLfsConnection_lfsurl_lfsconfigFromHEAD() + throws Exception { + writeLfsConfig(); + git.add().addFilepattern(Constants.DOT_LFS_CONFIG).call(); + git.commit().setMessage("Commit LFS Config").call(); + + /* + * reading .lfsconfig from HEAD seems only testable using a bare repo, + * since otherwise working tree or index are used + */ + File directory = createTempDirectory("testBareRepo"); + try (Repository bareRepoDb = Git.cloneRepository() + .setDirectory(directory) + .setURI(db.getDirectory().toURI().toString()).setBare(true) + .call().getRepository()) { + + checkLfsUrl(LFS_SERVER_URL1); + } + } + + @Test + public void checkGetLfsConnection_remote_lfsconfigFromWorkingDir() + throws Exception { + addRemoteUrl(ORIGIN_URL); + writeLfsConfig(LFS_SERVER_URL1, "lfs", DEFAULT_REMOTE_NAME, "url"); + checkLfsUrl(LFS_SERVER_URL1); + } + + /** + * Test the config file precedence. + * + * Checking only with the local repository config is sufficient since from + * that point the "normal" precedence is used. + * + * @throws Exception + */ + @Test + public void checkGetLfsConnection_ConfigFilePrecedence_lfsconfigFromWorkingDir() + throws Exception { + writeLfsConfig(); + checkLfsUrl(LFS_SERVER_URL1); + + StoredConfig config = git.getRepository().getConfig(); + config.setString(ConfigConstants.CONFIG_SECTION_LFS, null, + ConfigConstants.CONFIG_KEY_URL, LFS_SERVER_URL2); + config.save(); + + checkLfsUrl(LFS_SERVER_URL2); + } + + @Test + public void checkGetLfsConnection_InvalidLfsConfig_WorkingDir() + throws Exception { + writeInvalidLfsConfig(); + LfsConfigInvalidException actualException = assertThrows( + LfsConfigInvalidException.class, () -> { + LfsConnectionFactory.getLfsConnection(db, HttpSupport.METHOD_POST, + Protocol.OPERATION_DOWNLOAD); + }); + assertTrue(getStackTrace(actualException) + .contains("Invalid line in config file")); + } + + @Test + public void checkGetLfsConnection_InvalidLfsConfig_Index() + throws Exception { + writeInvalidLfsConfig(); + git.add().addFilepattern(Constants.DOT_LFS_CONFIG).call(); + deleteTrashFile(Constants.DOT_LFS_CONFIG); + LfsConfigInvalidException actualException = assertThrows( + LfsConfigInvalidException.class, () -> { + LfsConnectionFactory.getLfsConnection(db, HttpSupport.METHOD_POST, + Protocol.OPERATION_DOWNLOAD); + }); + assertTrue(getStackTrace(actualException) + .contains("Invalid line in config file")); + } + + @Test + public void checkGetLfsConnection_InvalidLfsConfig_HEAD() throws Exception { + writeInvalidLfsConfig(); + git.add().addFilepattern(Constants.DOT_LFS_CONFIG).call(); + git.commit().setMessage("Commit LFS Config").call(); + + /* + * reading .lfsconfig from HEAD seems only testable using a bare repo, + * since otherwise working tree or index are used + */ + File directory = createTempDirectory("testBareRepo"); + try (Repository bareRepoDb = Git.cloneRepository() + .setDirectory(directory) + .setURI(db.getDirectory().toURI().toString()).setBare(true) + .call().getRepository()) { + LfsConfigInvalidException actualException = assertThrows( + LfsConfigInvalidException.class, + () -> { + LfsConnectionFactory.getLfsConnection(db, + HttpSupport.METHOD_POST, + Protocol.OPERATION_DOWNLOAD); + }); + assertTrue(getStackTrace(actualException) + .contains("Invalid line in config file")); + } + } + + private void addRemoteUrl(String remotUrl) throws Exception { + RemoteAddCommand add = git.remoteAdd(); + add.setUri(new URIish(remotUrl)); + add.setName(org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME); + add.call(); + } + + /** + * Returns the stack trace of the provided exception as string + * + * @param actualException + * @return The exception stack trace as string + */ + private String getStackTrace(Exception actualException) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + actualException.printStackTrace(pw); + return sw.toString(); + } + + private void writeLfsConfig() throws IOException { + writeLfsConfig(LFS_SERVER_URL1, "lfs", "url"); + } + + private void writeLfsConfig(String lfsUrl, String section, String name) + throws IOException { + writeLfsConfig(lfsUrl, section, null, name); + } + + /* + * Write simple lfs config with single entry. Do not use FileBasedConfig to + * avoid introducing new dependency (for now). + */ + private void writeLfsConfig(String lfsUrl, String section, + String subsection, String name) throws IOException { + StringBuilder config = new StringBuilder(); + config.append("["); + config.append(section); + if (subsection != null) { + config.append(" \""); + config.append(subsection); + config.append("\""); + } + config.append("]\n"); + config.append(" "); + config.append(name); + config.append(" = "); + config.append(lfsUrl); + writeTrashFile(Constants.DOT_LFS_CONFIG, config.toString()); + } + + private void writeInvalidLfsConfig() throws IOException { + writeTrashFile(Constants.DOT_LFS_CONFIG, + "{lfs]\n url = " + LFS_SERVER_URL1); + } + + private void checkLfsUrl(String lfsUrl) throws IOException { + HttpConnection lfsServerConn; + lfsServerConn = LfsConnectionFactory.getLfsConnection(db, + HttpSupport.METHOD_POST, Protocol.OPERATION_DOWNLOAD); + + assertEquals(lfsUrl + Protocol.OBJECTS_LFS_ENDPOINT, + lfsServerConn.getURL().toString()); + } +} diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF index fba58f8394..09ffffbf86 100644 --- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF @@ -3,31 +3,33 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.lfs Bundle-SymbolicName: org.eclipse.jgit.lfs -Bundle-Version: 6.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.jgit.lfs;version="6.0.1", - org.eclipse.jgit.lfs.errors;version="6.0.1", - org.eclipse.jgit.lfs.internal;version="6.0.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.0.1" +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" 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.0.1,6.1.0)";resolution:=optional, - org.eclipse.jgit.api.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.attributes;version="[6.0.1,6.1.0)", - org.eclipse.jgit.diff;version="[6.0.1,6.1.0)", - org.eclipse.jgit.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.hooks;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.nls;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.storage.pack;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.http;version="[6.0.1,6.1.0)", - org.eclipse.jgit.treewalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.treewalk.filter;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util.io;version="[6.0.1,6.1.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)" diff --git a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF index a715be6868..7a2a446c48 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml index eb57db373c..949c568217 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 0e00f146ae..642b83db44 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 @@ -1,19 +1,19 @@ corruptLongObject=The content hash ''{0}'' of the long object ''{1}'' doesn''t match its id, the corrupt object will be deleted. -incorrectLONG_OBJECT_ID_LENGTH=Incorrect LONG_OBJECT_ID_LENGTH. -inconsistentMediafileLength=Mediafile {0} has unexpected length; expected {1} but found {2}. +dotLfsConfigReadFailed=Reading .lfsconfig failed inconsistentContentLength=Unexpected content length reported by LFS server ({0}), expected {1} but reported was {2} +inconsistentMediafileLength=Mediafile {0} has unexpected length; expected {1} but found {2}. +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" +lfsUnauthorized=Not authorized to perform operation {0} on repository {1} lfsUnavailable=LFS is not available for repository {0} +missingLocalObject="Local Object {0} is missing" protocolError=LFS Protocol Error {0}: {1} -requiredHashFunctionNotAvailable=Required hash function {0} not available. repositoryNotFound=Repository {0} not found repositoryReadOnly=Repository {0} is read-only -lfsUnavailable=LFS is not available for repository {0} -lfsUnathorized=Not authorized to perform operation {0} on repository {1} -lfsFailedToGetRepository=failed to get repository {0} -lfsNoDownloadUrl="Need to download object from LFS server but couldn't determine LFS server URL" +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} -wrongAmoutOfDataReceived=While downloading data from the content server {0} {1} bytes have been received while {2} have been expected userConfigInvalid="User config file {0} invalid {1}" -missingLocalObject="Local Object {0} is missing"
\ No newline at end of file +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 d6ce855794..ebf46e080e 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 @@ -113,6 +113,9 @@ public class LfsPrePushHook extends PrePushHook { try (ObjectWalk walk = new ObjectWalk(getRepository())) { for (RemoteRefUpdate up : refs) { + if (up.isDelete()) { + continue; + } walk.setRewriteParents(false); excludeRemoteRefs(walk); walk.markStart(walk.parseCommit(up.getNewObjectId())); diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java index 3411887567..c26a1bfbb3 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java @@ -205,7 +205,7 @@ public class SmudgeFilter extends FilterCommand { long bytesCopied = Files.copy(contentIn, path); if (bytesCopied != o.size) { throw new IOException(MessageFormat.format( - LfsText.get().wrongAmoutOfDataReceived, + LfsText.get().wrongAmountOfDataReceived, contentServerConn.getURL(), Long.valueOf(bytesCopied), Long.valueOf(o.size))); diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsUnauthorized.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsUnauthorized.java index 36889db8a6..0dc6aeab29 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsUnauthorized.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsUnauthorized.java @@ -31,7 +31,7 @@ public class LfsUnauthorized extends LfsException { * the repository name. */ public LfsUnauthorized(String operation, String name) { - super(MessageFormat.format(LfsText.get().lfsUnathorized, operation, + super(MessageFormat.format(LfsText.get().lfsUnauthorized, operation, name)); } } 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 new file mode 100644 index 0000000000..71d395ca84 --- /dev/null +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConfig.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2022, Matthias Fromme <mfromme@dspace.de> + * + * 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.lfs.internal; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lfs.errors.LfsConfigInvalidException; +import org.eclipse.jgit.lfs.lib.Constants; +import org.eclipse.jgit.lib.BlobBasedConfig; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevTree; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.treewalk.TreeWalk; + +import static org.eclipse.jgit.lib.Constants.HEAD; + +/** + * Encapsulate access to the .lfsconfig. + * + * 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. + */ +public class LfsConfig { + private Repository db; + private Config delegate; + + /** + * Create a new instance of the LfsConfig. + * + * @param db + * the associated repo + * @throws IOException + */ + public LfsConfig(Repository db) throws IOException { + this.db = db; + delegate = this.load(); + } + + /** + * Read the .lfsconfig file from the repository + * + * @return The loaded lfs config or null if it does not exist + * + * @throws IOException + */ + private Config load() throws IOException { + Config result = null; + + if (!db.isBare()) { + result = loadFromWorkingTree(); + if (result == null) { + result = loadFromIndex(); + } + } + + if (result == null) { + result = loadFromHead(); + } + + if (result == null) { + result = emptyConfig(); + } + + return result; + } + + /** + * Try to read the lfs config from a file called .lfsconfig at the top level + * of the working tree. + * + * @return the config, or <code>null</code> + * @throws IOException + */ + @Nullable + private Config loadFromWorkingTree() + throws IOException { + File lfsConfig = db.getFS().resolve(db.getWorkTree(), + Constants.DOT_LFS_CONFIG); + if (lfsConfig.exists() && lfsConfig.isFile()) { + FileBasedConfig config = new FileBasedConfig(lfsConfig, db.getFS()); + try { + config.load(); + return config; + } catch (ConfigInvalidException e) { + throw new LfsConfigInvalidException( + LfsText.get().dotLfsConfigReadFailed, e); + } + } + return null; + } + + /** + * Try to read the lfs config from an entry called .lfsconfig contained in + * the index. + * + * @return the config, or <code>null</code> if the entry does not exist + * @throws IOException + */ + @Nullable + private Config loadFromIndex() + throws IOException { + try { + DirCacheEntry entry = db.readDirCache() + .getEntry(Constants.DOT_LFS_CONFIG); + if (entry != null) { + return new BlobBasedConfig(null, db, entry.getObjectId()); + } + } catch (ConfigInvalidException e) { + throw new LfsConfigInvalidException( + LfsText.get().dotLfsConfigReadFailed, e); + } + return null; + } + + /** + * Try to read the lfs config from an entry called .lfsconfig contained in + * the head revision. + * + * @return the config, or <code>null</code> if the file does not exist + * @throws IOException + */ + @Nullable + private Config loadFromHead() throws IOException { + try (RevWalk revWalk = new RevWalk(db)) { + ObjectId headCommitId = db.resolve(HEAD); + if (headCommitId == null) { + return null; + } + RevCommit commit = revWalk.parseCommit(headCommitId); + RevTree tree = commit.getTree(); + TreeWalk treewalk = TreeWalk.forPath(db, Constants.DOT_LFS_CONFIG, + tree); + if (treewalk != null) { + return new BlobBasedConfig(null, db, treewalk.getObjectId(0)); + } + } catch (ConfigInvalidException e) { + throw new LfsConfigInvalidException( + LfsText.get().dotLfsConfigReadFailed, e); + } + return null; + } + + /** + * Create an empty config as fallback to avoid null pointer checks. + * + * @return an empty config + */ + private Config emptyConfig() { + return new Config(); + } + + /** + * Get string value or null if not found. + * + * First tries to find the value in the git config files. If not found tries + * to find data in .lfsconfig. + * + * @param section + * the section + * @param subsection + * the subsection for the value + * @param name + * the key name + * @return a String value from the config, <code>null</code> if not found + */ + public String getString(final String section, final String subsection, + final String name) { + String result = db.getConfig().getString(section, subsection, name); + if (result == null) { + result = delegate.getString(section, subsection, name); + } + return result; + } +} diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java index e221913bea..12b688d157 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java @@ -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 @@ -39,12 +39,12 @@ import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.transport.http.HttpConnection; import org.eclipse.jgit.util.HttpSupport; import org.eclipse.jgit.util.SshSupport; +import org.eclipse.jgit.util.StringUtils; /** * Provides means to get a valid LFS connection for a given repository. */ public class LfsConnectionFactory { - private static final int SSH_AUTH_TIMEOUT_SECONDS = 30; private static final String SCHEME_HTTPS = "https"; //$NON-NLS-1$ private static final String SCHEME_SSH = "ssh"; //$NON-NLS-1$ @@ -64,7 +64,7 @@ public class LfsConnectionFactory { * be used for * @param purpose * the action, e.g. Protocol.OPERATION_DOWNLOAD - * @return the url for the lfs server. e.g. + * @return the connection for the lfs server. e.g. * "https://github.com/github/git-lfs.git/info/lfs" * @throws IOException */ @@ -92,13 +92,30 @@ public class LfsConnectionFactory { return connection; } + /** + * Get LFS Server URL. + * + * @param db + * the repository to work with + * @param purpose + * the action, e.g. Protocol.OPERATION_DOWNLOAD + * @param additionalHeaders + * additional headers that can be used to connect to LFS server + * @return the URL for the LFS server. e.g. + * "https://github.com/github/git-lfs.git/info/lfs" + * @throws IOException + * if the LFS config is invalid or cannot be accessed + * @see <a href= + * "https://github.com/git-lfs/git-lfs/blob/main/docs/api/server-discovery.md"> + * Server Discovery documentation</a> + */ private static String getLfsUrl(Repository db, String purpose, Map<String, String> additionalHeaders) - throws LfsConfigInvalidException { - StoredConfig config = db.getConfig(); + throws IOException { + LfsConfig config = new LfsConfig(db); String lfsUrl = config.getString(ConfigConstants.CONFIG_SECTION_LFS, - null, - ConfigConstants.CONFIG_KEY_URL); + null, ConfigConstants.CONFIG_KEY_URL); + Exception ex = null; if (lfsUrl == null) { String remoteUrl = null; @@ -106,6 +123,7 @@ public class LfsConnectionFactory { lfsUrl = config.getString(ConfigConstants.CONFIG_SECTION_LFS, remote, ConfigConstants.CONFIG_KEY_URL); + // This could be done better (more precise logic), but according // to https://github.com/git-lfs/git-lfs/issues/1759 git-lfs // generally only supports 'origin' in an integrated workflow. @@ -125,8 +143,6 @@ public class LfsConnectionFactory { | CommandFailedException e) { ex = e; } - } else { - lfsUrl = lfsUrl + Protocol.INFO_LFS_ENDPOINT; } } if (lfsUrl == null) { @@ -149,7 +165,8 @@ public class LfsConnectionFactory { additionalHeaders.putAll(action.header); return action.href; } - return remoteUrl + Protocol.INFO_LFS_ENDPOINT; + return StringUtils.nameWithDotGit(remoteUrl) + + Protocol.INFO_LFS_ENDPOINT; } private static Protocol.ExpiringAction getSshAuthentication( 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 1ca37a9f66..06234c1d90 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 @@ -28,21 +28,22 @@ public class LfsText extends TranslationBundle { // @formatter:off /***/ public String corruptLongObject; - /***/ public String inconsistentMediafileLength; + /***/ public String dotLfsConfigReadFailed; /***/ public String inconsistentContentLength; + /***/ public String inconsistentMediafileLength; /***/ public String incorrectLONG_OBJECT_ID_LENGTH; /***/ public String invalidLongId; /***/ public String invalidLongIdLength; + /***/ public String lfsFailedToGetRepository; + /***/ public String lfsNoDownloadUrl; + /***/ public String lfsUnauthorized; /***/ public String lfsUnavailable; + /***/ public String missingLocalObject; /***/ public String protocolError; - /***/ public String requiredHashFunctionNotAvailable; /***/ public String repositoryNotFound; /***/ public String repositoryReadOnly; - /***/ public String lfsUnathorized; - /***/ public String lfsFailedToGetRepository; - /***/ public String lfsNoDownloadUrl; + /***/ public String requiredHashFunctionNotAvailable; /***/ public String serverFailure; - /***/ public String wrongAmoutOfDataReceived; /***/ public String userConfigInvalid; - /***/ public String missingLocalObject; + /***/ public String wrongAmountOfDataReceived; } diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java index 3212a63504..9b41ec31f1 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java @@ -82,6 +82,13 @@ public final class Constants { public static final String ATTR_FILTER_DRIVER_PREFIX = "lfs/"; /** + * Config file name for lfs specific configuration + * + * @since 6.1 + */ + public static final String DOT_LFS_CONFIG = ".lfsconfig"; + + /** * Create a new digest function for objects. * * @return a new digest object. 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 cc1bf85f72..cd22e24ba8 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.0.1.qualifier" + version="6.1.1.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 874b274f54..54feb9d384 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml index be0266260b..2061948432 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.0.1.qualifier" + version="6.1.1.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -23,7 +23,7 @@ </url> <requires> - <import plugin="org.eclipse.jgit" version="6.0.1" match="equivalent"/> + <import plugin="org.eclipse.jgit" version="6.1.1" 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 2909e808cf..760903e7e0 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 8937a83391..e899ad56d3 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.0.1.qualifier" + version="6.1.1.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -23,7 +23,7 @@ </url> <requires> - <import plugin="org.eclipse.jgit" version="6.0.1" match="equivalent"/> + <import plugin="org.eclipse.jgit" version="6.1.1" 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 2292c2eee8..b725dff216 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 6eee7cde11..99073e4710 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.0.1.qualifier" + version="6.1.1.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.0.1" match="equivalent"/> + <import plugin="org.eclipse.jgit" version="6.1.1" 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 dc4159bc83..3566dbb4a0 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 f0a488452e..c04051b604 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.0.1.qualifier" + version="6.1.1.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -23,7 +23,7 @@ </url> <requires> - <import feature="org.eclipse.jgit" version="6.0.1" match="equivalent"/> + <import feature="org.eclipse.jgit" version="6.1.1" 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 e344c19f35..50338a60c5 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 f5889e55ac..3939f51b9b 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.0.1.qualifier" + version="6.1.1.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.0.1" match="equivalent"/> - <import feature="org.eclipse.jgit.lfs" version="6.0.1" match="equivalent"/> - <import feature="org.eclipse.jgit.ssh.apache" version="6.0.1" match="equivalent"/> + <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"/> </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 ae9570440e..4b0e26aa94 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 e12e5997da..9ccf39eafa 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 d5d1554cfd..d3e662b9ba 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.0.1.qualifier" + version="6.1.1.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -23,7 +23,7 @@ </url> <requires> - <import feature="org.eclipse.jgit" version="6.0.1" match="equivalent"/> + <import feature="org.eclipse.jgit" version="6.1.1" 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 a20645e8b6..d8b5de2436 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.0.1-SNAPSHOT</version> + <version>6.1.1-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.0.1-SNAPSHOT</version> + <version>6.1.1-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 ae62fa064a..e03964727e 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.0.1.qualifier" + version="6.1.1.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -23,7 +23,7 @@ </url> <requires> - <import feature="org.eclipse.jgit" version="6.0.1" match="equivalent"/> + <import feature="org.eclipse.jgit" version="6.1.1" 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 6f12afab13..591b448016 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml index df42e93e46..3df4a9284a 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.0.1.qualifier" + version="6.1.1.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -23,7 +23,7 @@ </url> <requires> - <import plugin="org.eclipse.jgit" version="6.0.1" match="equivalent"/> + <import plugin="org.eclipse.jgit" version="6.1.1" 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 37ada5d77e..e461bff45a 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> 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 d31cd37599..5d61eefb64 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.0.1.qualifier +Bundle-Version: 6.1.1.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 deleted file mode 100644 index 91370cc2f6..0000000000 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target +++ /dev/null @@ -1,97 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<?pde?> -<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> -<target name="jgit-4.17" sequenceNumber="1641393954"> - <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.8.v20211029-0838"/> - <unit id="com.google.gson.source" version="2.8.8.v20211029-0838"/> - <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.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.v20210923-1401"/> - <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20210923-1401"/> - <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.14.v20210128-2225"/> - <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/> - <unit id="org.apache.sshd.osgi" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.osgi.source" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.sftp" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.sftp.source" version="2.7.0.v20210623-0618"/> - <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.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpg.source" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpkix" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpkix.source" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcprov" version="1.69.0.v20210923-1401"/> - <unit id="org.bouncycastle.bcprov.source" version="1.69.0.v20210923-1401"/> - <unit id="org.bouncycastle.bcutil" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcutil.source" version="1.69.0.v20210713-1924"/> - <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/R20211213173813/repository"/> - </location> - <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> - <unit id="org.eclipse.osgi" version="0.0.0"/> - <repository location="https://download.eclipse.org/releases/2020-09/"/> - </location> - </locations> -</target> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd deleted file mode 100644 index c2b13cc29c..0000000000 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd +++ /dev/null @@ -1,8 +0,0 @@ -target "jgit-4.17" with source configurePhase - -include "projects/jetty-10.0.x.tpd" -include "orbit/R20211213173813-2021-12.tpd" - -location "https://download.eclipse.org/releases/2020-09/" { - org.eclipse.osgi lazy -} diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target deleted file mode 100644 index 28204f5384..0000000000 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target +++ /dev/null @@ -1,97 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<?pde?> -<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> -<target name="jgit-4.18" sequenceNumber="1641394122"> - <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.8.v20211029-0838"/> - <unit id="com.google.gson.source" version="2.8.8.v20211029-0838"/> - <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.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.v20210923-1401"/> - <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20210923-1401"/> - <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.14.v20210128-2225"/> - <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/> - <unit id="org.apache.sshd.osgi" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.osgi.source" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.sftp" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.sftp.source" version="2.7.0.v20210623-0618"/> - <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.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpg.source" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpkix" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpkix.source" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcprov" version="1.69.0.v20210923-1401"/> - <unit id="org.bouncycastle.bcprov.source" version="1.69.0.v20210923-1401"/> - <unit id="org.bouncycastle.bcutil" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcutil.source" version="1.69.0.v20210713-1924"/> - <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/R20211213173813/repository"/> - </location> - <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> - <unit id="org.eclipse.osgi" version="0.0.0"/> - <repository location="https://download.eclipse.org/releases/2020-12/"/> - </location> - </locations> -</target> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd deleted file mode 100644 index ff6741d8b4..0000000000 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd +++ /dev/null @@ -1,8 +0,0 @@ -target "jgit-4.18" with source configurePhase - -include "projects/jetty-10.0.x.tpd" -include "orbit/R20211213173813-2021-12.tpd" - -location "https://download.eclipse.org/releases/2020-12/" { - org.eclipse.osgi lazy -} diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target deleted file mode 100644 index 20b052992d..0000000000 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target +++ /dev/null @@ -1,97 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<?pde?> -<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> -<target name="jgit-4.19-staging" sequenceNumber="1641394141"> - <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.8.v20211029-0838"/> - <unit id="com.google.gson.source" version="2.8.8.v20211029-0838"/> - <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.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.v20210923-1401"/> - <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20210923-1401"/> - <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.14.v20210128-2225"/> - <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/> - <unit id="org.apache.sshd.osgi" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.osgi.source" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.sftp" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.sftp.source" version="2.7.0.v20210623-0618"/> - <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.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpg.source" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpkix" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpkix.source" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcprov" version="1.69.0.v20210923-1401"/> - <unit id="org.bouncycastle.bcprov.source" version="1.69.0.v20210923-1401"/> - <unit id="org.bouncycastle.bcutil" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcutil.source" version="1.69.0.v20210713-1924"/> - <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/R20211213173813/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/2021-03/"/> - </location> - </locations> -</target> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd deleted file mode 100644 index 8fcaa4733e..0000000000 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd +++ /dev/null @@ -1,8 +0,0 @@ -target "jgit-4.19-staging" with source configurePhase - -include "projects/jetty-10.0.x.tpd" -include "orbit/R20211213173813-2021-12.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 deleted file mode 100644 index 153314d526..0000000000 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target +++ /dev/null @@ -1,97 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<?pde?> -<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> -<target name="jgit-4.20" sequenceNumber="1641394152"> - <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.8.v20211029-0838"/> - <unit id="com.google.gson.source" version="2.8.8.v20211029-0838"/> - <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.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.v20210923-1401"/> - <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20210923-1401"/> - <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.14.v20210128-2225"/> - <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/> - <unit id="org.apache.sshd.osgi" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.osgi.source" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.sftp" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.sftp.source" version="2.7.0.v20210623-0618"/> - <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.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpg.source" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpkix" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpkix.source" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcprov" version="1.69.0.v20210923-1401"/> - <unit id="org.bouncycastle.bcprov.source" version="1.69.0.v20210923-1401"/> - <unit id="org.bouncycastle.bcutil" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcutil.source" version="1.69.0.v20210713-1924"/> - <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/R20211213173813/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/2021-06/"/> - </location> - </locations> -</target> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd deleted file mode 100644 index 33adb7264c..0000000000 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd +++ /dev/null @@ -1,8 +0,0 @@ -target "jgit-4.20" with source configurePhase - -include "projects/jetty-10.0.x.tpd" -include "orbit/R20211213173813-2021-12.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 07de1af89b..edc16cd0a7 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="1641394170"> +<target name="jgit-4.21" sequenceNumber="1753103951"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="jakarta.servlet-api" version="4.0.0"/> @@ -23,8 +23,8 @@ <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.8.v20211029-0838"/> - <unit id="com.google.gson.source" version="2.8.8.v20211029-0838"/> + <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.1.v201205102305"/> @@ -51,22 +51,22 @@ <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/> <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/> <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/> - <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/> - <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/> - <unit id="org.apache.sshd.osgi" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.osgi.source" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.sftp" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.sftp.source" version="2.7.0.v20210623-0618"/> + <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.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpg.source" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpkix" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpkix.source" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcprov" version="1.69.0.v20210923-1401"/> - <unit id="org.bouncycastle.bcprov.source" version="1.69.0.v20210923-1401"/> - <unit id="org.bouncycastle.bcutil" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcutil.source" version="1.69.0.v20210713-1924"/> + <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.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.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"/> @@ -87,11 +87,11 @@ <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/R20211213173813/repository"/> + <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220302172233/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/2021-09/"/> + <repository location="https://download.eclipse.org/releases/2021-12/"/> </location> </locations> </target> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd index adf62d5270..43ca5f5b66 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,8 +1,8 @@ target "jgit-4.21" with source configurePhase include "projects/jetty-10.0.x.tpd" -include "orbit/R20211213173813-2021-12.tpd" +include "orbit/R20220302172233-2022-03.tpd" -location "https://download.eclipse.org/releases/2021-09/" { +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.22.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target index 97b5fb69c1..b229da160b 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="1641394177"> +<target name="jgit-4.22" sequenceNumber="1646256653"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="jakarta.servlet-api" version="4.0.0"/> @@ -23,8 +23,8 @@ <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.8.v20211029-0838"/> - <unit id="com.google.gson.source" version="2.8.8.v20211029-0838"/> + <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.1.v201205102305"/> @@ -51,22 +51,22 @@ <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/> <unit id="org.apache.httpcomponents.httpclient" version="4.5.13.v20210128-2225"/> <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.13.v20210128-2225"/> - <unit id="org.apache.httpcomponents.httpcore" version="4.4.14.v20210128-2225"/> - <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.14.v20210128-2225"/> - <unit id="org.apache.sshd.osgi" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.osgi.source" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.sftp" version="2.7.0.v20210623-0618"/> - <unit id="org.apache.sshd.sftp.source" version="2.7.0.v20210623-0618"/> + <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.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpg.source" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpkix" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcpkix.source" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcprov" version="1.69.0.v20210923-1401"/> - <unit id="org.bouncycastle.bcprov.source" version="1.69.0.v20210923-1401"/> - <unit id="org.bouncycastle.bcutil" version="1.69.0.v20210713-1924"/> - <unit id="org.bouncycastle.bcutil.source" version="1.69.0.v20210713-1924"/> + <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.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.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"/> @@ -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/R20211213173813/repository"/> + <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220302172233/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 37c93a78e8..eb1723c736 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/R20211213173813-2021-12.tpd" +include "orbit/R20220302172233-2022-03.tpd" location "https://download.eclipse.org/releases/2021-12/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220302172233-2022-03.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220302172233-2022-03.tpd new file mode 100644 index 0000000000..fafc689268 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220302172233-2022-03.tpd @@ -0,0 +1,69 @@ +target "R20220302172233" with source configurePhase +// see https://download.eclipse.org/tools/orbit/downloads/ + +location "https://download.eclipse.org/tools/orbit/downloads/drops/R20220302172233/repository" { + com.google.gson [2.8.9.v20220111-1409,2.8.9.v20220111-1409] + com.google.gson.source [2.8.9.v20220111-1409,2.8.9.v20220111-1409] + com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902] + com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902] + com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305] + com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305] + com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343] + com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343] + com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004] + com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004] + javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839] + javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839] + net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410] + net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534] + net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534] + net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410] + net.i2p.crypto.eddsa [0.3.0.v20210923-1401,0.3.0.v20210923-1401] + net.i2p.crypto.eddsa.source [0.3.0.v20210923-1401,0.3.0.v20210923-1401] + org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452] + org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452] + org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422] + org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422] + org.apache.commons.compress [1.21.0.v20211103-2100,1.21.0.v20211103-2100] + org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100] + org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502] + org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502] + org.apache.httpcomponents.httpclient [4.5.13.v20210128-2225,4.5.13.v20210128-2225] + org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225] + org.apache.httpcomponents.httpcore [4.4.15.v20220209-2345,4.4.15.v20220209-2345] + org.apache.httpcomponents.httpcore.source [4.4.15.v20220209-2345,4.4.15.v20220209-2345] + org.apache.sshd.osgi [2.8.0.v20211227-1750,2.8.0.v20211227-1750] + org.apache.sshd.osgi.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750] + org.apache.sshd.sftp [2.8.0.v20211227-1750,2.8.0.v20211227-1750] + org.apache.sshd.sftp.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750] + org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104] + org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104] + org.bouncycastle.bcpg [1.70.0.v20220105-1522,1.70.0.v20220105-1522] + org.bouncycastle.bcpg.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522] + org.bouncycastle.bcpkix [1.70.0.v20220105-1522,1.70.0.v20220105-1522] + org.bouncycastle.bcpkix.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522] + org.bouncycastle.bcprov [1.70.0.v20220105-1522,1.70.0.v20220105-1522] + org.bouncycastle.bcprov.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522] + org.bouncycastle.bcutil [1.70.0.v20220105-1522,1.70.0.v20220105-1522] + org.bouncycastle.bcutil.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522] + org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821] + org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821] + org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519] + org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519] + org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246] + org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246] + org.junit [4.13.2.v20211018-1956,4.13.2.v20211018-1956] + org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956] + org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218] + org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218] + org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642] + org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642] + org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519] + org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519] + org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150] + org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150] + org.slf4j.binding.simple [1.7.30.v20200204-2150,1.7.30.v20200204-2150] + org.slf4j.binding.simple.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150] + org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259] + org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259] +} diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml index 8bee1fc4f6..2a95042eed 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml @@ -16,7 +16,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.target</artifactId> diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml index 80a64bdd82..0c05369307 100644 --- a/org.eclipse.jgit.packaging/pom.xml +++ b/org.eclipse.jgit.packaging/pom.xml @@ -16,16 +16,15 @@ <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> <packaging>pom</packaging> <name>JGit Tycho Parent</name> <properties> <java.version>11</java.version> - <tycho-version>2.5.0</tycho-version> - <tycho-extras-version>${tycho-version}</tycho-extras-version> - <target-platform>jgit-4.17</target-platform> + <tycho-version>4.0.13</tycho-version> + <target-platform>jgit-4.21</target-platform> </properties> <pluginRepositories> @@ -187,6 +186,19 @@ </rules> </configuration> </execution> + <execution> + <id>enforce-java</id> + <goals> + <goal>enforce</goal> + </goals> + <configuration> + <rules> + <requireJavaVersion> + <version>17</version> + </requireJavaVersion> + </rules> + </configuration> + </execution> </executions> </plugin> <plugin> diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF index af00b0ae8a..f5c4de477c 100644 --- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF @@ -3,28 +3,30 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.pgm.test Bundle-SymbolicName: org.eclipse.jgit.pgm.test -Bundle-Version: 6.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 -Import-Package: org.eclipse.jgit.api;version="[6.0.1,6.1.0)", - org.eclipse.jgit.api.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.diff;version="[6.0.1,6.1.0)", - org.eclipse.jgit.dircache;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.file;version="6.0.1", - org.eclipse.jgit.junit;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.merge;version="[6.0.1,6.1.0)", - org.eclipse.jgit.pgm;version="[6.0.1,6.1.0)", - org.eclipse.jgit.pgm.internal;version="[6.0.1,6.1.0)", - org.eclipse.jgit.pgm.opt;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.treewalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util.io;version="[6.0.1,6.1.0)", +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)", org.junit;version="[4.13,5.0.0)", org.junit.rules;version="[4.13,5.0.0)", diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml index cd2bb63206..c824788255 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.pgm.test</artifactId> diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java index 4bad73b5b5..c78544309b 100644 --- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java @@ -89,6 +89,31 @@ public class DescribeTest extends CLIRepositoryTestCase { } @Test + public void testDescribeCommitMatchAbbrev() throws Exception { + initialCommitAndTag(); + secondCommit(); + assertArrayEquals(new String[] { "v1.0-1-g56f6cebdf3f5", "" }, + execute("git describe --abbrev 12 --match v1.*")); + } + + @Test + public void testDescribeCommitMatchAbbrevMin() throws Exception { + initialCommitAndTag(); + secondCommit(); + assertArrayEquals(new String[] { "v1.0-1-g56f6", "" }, + execute("git describe --abbrev -5 --match v1.*")); + } + + @Test + public void testDescribeCommitMatchAbbrevMax() throws Exception { + initialCommitAndTag(); + secondCommit(); + assertArrayEquals(new String[] { + "v1.0-1-g56f6cebdf3f5ceeecd803365abf0996fb1fa006d", "" }, + execute("git describe --abbrev 50 --match v1.*")); + } + + @Test public void testDescribeCommitMatch2() throws Exception { initialCommitAndTag(); secondCommit(); 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 new file mode 100644 index 0000000000..e7bf48417d --- /dev/null +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DiffToolTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2021, 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 java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +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.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; + + @Argument(index = 1, metaVar = "metaVar_arg") + List<String> arguments = new ArrayList<>(); + } + + private String[] runAndCaptureUsingInitRaw(String... args) + throws Exception { + 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, null, 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(); + } + return result.outLines().toArray(new String[0]); + } + + private Git git; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + git = new Git(db); + git.commit().setMessage("initial commit").call(); + } + + @Test + public void testTool() throws Exception { + RevCommit commit = createUnstagedChanges(); + List<DiffEntry> changes = getRepositoryChanges(commit); + String[] expectedOutput = getExpectedDiffToolOutput(changes); + + String[] options = { + "--tool", + "-t", + }; + + for (String option : options) { + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, + runAndCaptureUsingInitRaw("difftool", option, + "some_tool")); + } + } + + @Test + public void testToolTrustExitCode() throws Exception { + RevCommit commit = createUnstagedChanges(); + List<DiffEntry> changes = getRepositoryChanges(commit); + String[] expectedOutput = getExpectedDiffToolOutput(changes); + + String[] options = { "--tool", "-t", }; + + for (String option : options) { + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, runAndCaptureUsingInitRaw("difftool", + "--trust-exit-code", option, "some_tool")); + } + } + + @Test + public void testToolNoGuiNoPromptNoTrustExitcode() throws Exception { + RevCommit commit = createUnstagedChanges(); + List<DiffEntry> changes = getRepositoryChanges(commit); + String[] expectedOutput = getExpectedDiffToolOutput(changes); + + String[] options = { "--tool", "-t", }; + + for (String option : options) { + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, runAndCaptureUsingInitRaw("difftool", + "--no-gui", "--no-prompt", "--no-trust-exit-code", + option, "some_tool")); + } + } + + @Test + public void testToolCached() throws Exception { + RevCommit commit = createStagedChanges(); + List<DiffEntry> changes = getRepositoryChanges(commit); + String[] expectedOutput = getExpectedDiffToolOutput(changes); + + String[] options = { "--cached", "--staged", }; + + for (String option : options) { + assertArrayOfLinesEquals("Incorrect output for option: " + option, + expectedOutput, runAndCaptureUsingInitRaw("difftool", + option, "--tool", "some_tool")); + } + } + + @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(); + 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.", + }; + expectedOutput.addAll(Arrays.asList(userDefinedToolsHelp)); + + 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; + } + return expectedToolOutput; + } + + private static void assertArrayOfLinesEquals(String failMessage, + String[] expected, String[] actual) { + assertEquals(failMessage, toString(expected), toString(actual)); + } +} diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/FetchTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/FetchTest.java index 564bd5fa94..1c6b7839d3 100644 --- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/FetchTest.java +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/FetchTest.java @@ -33,6 +33,7 @@ public class FetchTest extends CLIRepositoryTestCase { git.commit().setMessage("initial commit").call(); Repository remoteRepository = createWorkRepository(); + addRepoToClose(remoteRepository); remoteGit = new Git(remoteRepository); // setup the first repository to fetch from the second repository diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF index ffaafc13c8..ed980b5006 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.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-11 @@ -14,47 +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.0.1,6.1.0)", - org.eclipse.jgit.api.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.archive;version="[6.0.1,6.1.0)", - org.eclipse.jgit.awtui;version="[6.0.1,6.1.0)", - org.eclipse.jgit.blame;version="[6.0.1,6.1.0)", - org.eclipse.jgit.diff;version="[6.0.1,6.1.0)", - org.eclipse.jgit.dircache;version="[6.0.1,6.1.0)", - org.eclipse.jgit.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.gitrepo;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.io;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.pack;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.reftable;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.server;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.server.fs;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs.server.s3;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.merge;version="[6.0.1,6.1.0)", - org.eclipse.jgit.nls;version="[6.0.1,6.1.0)", - org.eclipse.jgit.notes;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revplot;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk.filter;version="[6.0.1,6.1.0)", - org.eclipse.jgit.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.storage.pack;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.http.apache;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.resolver;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.ssh.jsch;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.sshd;version="[6.0.1,6.1.0)", - org.eclipse.jgit.treewalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.treewalk.filter;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util.io;version="[6.0.1,6.1.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.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.0.1"; +Export-Package: org.eclipse.jgit.console;version="6.1.1"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util", - org.eclipse.jgit.pgm;version="6.0.1"; + org.eclipse.jgit.pgm;version="6.1.1"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util.io, org.eclipse.jgit.awtui, @@ -66,14 +68,14 @@ Export-Package: org.eclipse.jgit.console;version="6.0.1"; org.eclipse.jgit.treewalk, org.eclipse.jgit.api, javax.swing", - org.eclipse.jgit.pgm.debug;version="6.0.1"; + org.eclipse.jgit.pgm.debug;version="6.1.1"; uses:="org.eclipse.jgit.util.io, org.eclipse.jgit.pgm, org.eclipse.jetty.servlet", - org.eclipse.jgit.pgm.internal;version="6.0.1"; + org.eclipse.jgit.pgm.internal;version="6.1.1"; x-friends:="org.eclipse.jgit.pgm.test, org.eclipse.jgit.test", - org.eclipse.jgit.pgm.opt;version="6.0.1"; + org.eclipse.jgit.pgm.opt;version="6.1.1"; 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 26269d752d..0a5dd88040 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="6.1.1.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 e645255e96..8c44764c63 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 @@ -12,6 +12,7 @@ org.eclipse.jgit.pgm.ConvertRefStorage org.eclipse.jgit.pgm.Daemon org.eclipse.jgit.pgm.Describe org.eclipse.jgit.pgm.Diff +org.eclipse.jgit.pgm.DiffTool org.eclipse.jgit.pgm.DiffTree org.eclipse.jgit.pgm.Fetch org.eclipse.jgit.pgm.Gc diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml index 05e0bcc173..c189dc78c8 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 ae10af2f61..c364376917 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,6 +58,9 @@ 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} doesNotExist={0} does not exist dontOverwriteLocalChanges=error: Your local changes to the following file would be overwritten by merge: everythingUpToDate=Everything up-to-date @@ -145,6 +148,7 @@ metaVar_s3StorageClass=STORAGE-CLASS metaVar_seconds=SECONDS metaVar_service=SERVICE metaVar_tagLocalUser=<GPG key ID> +metaVar_tool=TOOL metaVar_treeish=tree-ish metaVar_uriish=uri-ish metaVar_url=URL @@ -225,6 +229,7 @@ unmergedPaths=Unmerged paths: unsupportedOperation=Unsupported operation: {0} untrackedFiles=Untracked files: updating=Updating {0}..{1} +usage_Abbrev=Instead of using the default number of hexadecimal digits (which will vary according to the number of objects in the repository with a default of 7) of the abbreviated object name, use <n> digits, or as many digits as needed to form a unique object name. An <n> of 0 will suppress long format, only showing the closest tag. usage_Aggressive=This option will cause gc to more aggressively optimize the repository at the expense of taking much more time usage_AlwaysFallback=Show uniquely abbreviated commit object as fallback usage_bareClone=Make a bare Git repository. That is, instead of creating [DIRECTORY] and placing the administrative files in [DIRECTORY]/.git, make the [DIRECTORY] itself the $GIT_DIR. @@ -249,6 +254,8 @@ usage_DiffAlgorithms=Test performance of jgit's diff algorithms 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_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 usage_LFSPort=Server http port @@ -296,6 +303,7 @@ usage_ShowRef=List references in a local repository 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_UpdateRemoteRepositoryFromLocalRefs=Update remote repository from local refs usage_UseAll=Use all refs found in refs/ usage_UseTags=Use any tag including lightweight tags @@ -342,6 +350,7 @@ usage_deleteFullyMergedBranch=delete fully merged branch usage_date=date format, one of default, rfc, local, iso, short, raw (as defined by git-log(1) ), locale or localelocal (jgit extensions) 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_directoriesToExport=directories to export usage_disableTheServiceInAllRepositories=disable the service in all repositories usage_displayAListOfAllRegisteredJgitCommands=Display a list of all registered jgit commands @@ -396,6 +405,8 @@ usage_pathToXml=path to the repo manifest XML file usage_performFsckStyleChecksOnReceive=perform fsck style checks on receive usage_portNumberToListenOn=port number to listen on usage_printOnlyBranchesThatContainTheCommit=print only branches that contain the commit +usage_prompt=Prompt before each invocation of the diff tool. This is the default behaviour; the option is provided to override any configuration settings. +usage_noPrompt=Do not prompt before launching a diff tool. usage_pruneStaleTrackingRefs=prune stale tracking refs usage_pushUrls=push URLs are manipulated usage_quiet=don't show progress messages @@ -423,6 +434,8 @@ usage_srcPrefix=show the source prefix instead of "a/" usage_sshDriver=Selects the built-in ssh library to use, JSch or Apache MINA sshd. usage_symbolicVersionForTheProject=Symbolic version for the project usage_tags=fetch all tags +usage_trustExitCode=git-difftool invokes a diff tool individually on each file. Errors reported by the diff tool are ignored by default. Use --trust-exit-code to make git-difftool exit when an invoked diff tool returns a non-zero exit code.\ngit-difftool will forward the exit code of the invoked tool when --trust-exit-code is used. +usage_noTrustExitCode=This option can be used to override --trust-exit-code setting. usage_notags=do not fetch tags usage_tagAnnotated=create an annotated tag, unsigned unless -s or -u are given, or config tag.gpgSign is true usage_tagDelete=delete tag @@ -431,6 +444,7 @@ usage_tagMessage=create an annotated tag with the given message, unsigned unless usage_tagSign=create a signed annotated tag usage_tagNoSign=suppress signing the tag usage_tagVerify=Verify the GPG signature +usage_toolHelp=Print a list of diff tools that may be used with --tool. usage_untrackedFilesMode=show untracked files usage_updateRef=reference to update usage_updateRemoteRefsFromAnotherRepository=Update remote refs from another repository diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java index 2b49cf73d4..1a3a2f6f4b 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java @@ -15,6 +15,7 @@ package org.eclipse.jgit.pgm; import static java.lang.Integer.valueOf; import static java.lang.Long.valueOf; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH; import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH; import java.io.IOException; @@ -116,7 +117,8 @@ class Blame extends TextBuiltin { boolean autoAbbrev = abbrev == 0; if (abbrev == 0) { - abbrev = db.getConfig().getInt("core", "abbrev", 7); //$NON-NLS-1$ //$NON-NLS-2$ + abbrev = db.getConfig().getInt("core", "abbrev", //$NON-NLS-1$ //$NON-NLS-2$ + OBJECT_ID_ABBREV_STRING_LENGTH); } if (!showBlankBoundary) { root = db.getConfig().getBoolean("blame", "blankboundary", false); //$NON-NLS-1$ //$NON-NLS-2$ diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java index 8aa119a358..116db037d4 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java @@ -44,6 +44,9 @@ class Describe extends TextBuiltin { @Option(name = "--match", usage = "usage_Match", metaVar = "metaVar_pattern") private List<String> patterns = new ArrayList<>(); + @Option(name = "--abbrev", usage = "usage_Abbrev") + private Integer abbrev; + /** {@inheritDoc} */ @Override protected void run() { @@ -57,6 +60,9 @@ class Describe extends TextBuiltin { cmd.setTags(useTags); cmd.setAlways(always); cmd.setMatch(patterns.toArray(new String[0])); + if (abbrev != null) { + cmd.setAbbrev(abbrev.intValue()); + } String result = null; try { result = cmd.call(); 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 new file mode 100644 index 0000000000..128881779b --- /dev/null +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTool.java @@ -0,0 +1,257 @@ +/* + * 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.pgm; + +import static org.eclipse.jgit.lib.Constants.HEAD; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +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.concurrent.TimeUnit; + +import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.diff.DiffFormatter; +import org.eclipse.jgit.dircache.DirCacheIterator; +import org.eclipse.jgit.errors.AmbiguousObjectException; +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.RevisionSyntaxException; +import org.eclipse.jgit.internal.diffmergetool.DiffTools; +import org.eclipse.jgit.internal.diffmergetool.ExternalDiffTool; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Repository; +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.treewalk.AbstractTreeIterator; +import org.eclipse.jgit.treewalk.CanonicalTreeParser; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.filter.TreeFilter; +import org.eclipse.jgit.util.StringUtils; +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.Option; + +@Command(name = "difftool", common = true, usage = "usage_DiffTool") +class DiffTool extends TextBuiltin { + private DiffFormatter diffFmt; + + private DiffTools diffTools; + + @Argument(index = 0, metaVar = "metaVar_treeish") + private AbstractTreeIterator oldTree; + + @Argument(index = 1, metaVar = "metaVar_treeish") + private AbstractTreeIterator newTree; + + @Option(name = "--tool", aliases = { + "-t" }, metaVar = "metaVar_tool", usage = "usage_ToolForDiff") + private String toolName; + + @Option(name = "--cached", aliases = { "--staged" }, usage = "usage_cached") + private boolean cached; + + 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 BooleanTriState gui = BooleanTriState.UNSET; + + @Option(name = "--gui", aliases = { "-g" }, usage = "usage_DiffGuiTool") + void setGui(@SuppressWarnings("unused") boolean on) { + gui = BooleanTriState.TRUE; + } + + @Option(name = "--no-gui", usage = "usage_noGui") + void noGui(@SuppressWarnings("unused") boolean on) { + gui = BooleanTriState.FALSE; + } + + private BooleanTriState trustExitCode = BooleanTriState.UNSET; + + @Option(name = "--trust-exit-code", usage = "usage_trustExitCode") + void setTrustExitCode(@SuppressWarnings("unused") boolean on) { + trustExitCode = BooleanTriState.TRUE; + } + + @Option(name = "--no-trust-exit-code", usage = "usage_noTrustExitCode") + void noTrustExitCode(@SuppressWarnings("unused") boolean on) { + trustExitCode = BooleanTriState.FALSE; + } + + @Option(name = "--", metaVar = "metaVar_paths", handler = PathTreeFilterHandler.class) + private TreeFilter pathFilter = TreeFilter.ALL; + + @Override + protected void init(Repository repository, String gitDir) { + super.init(repository, gitDir); + diffFmt = new DiffFormatter(new BufferedOutputStream(outs)); + diffTools = new DiffTools(repository); + } + + @Override + protected void run() { + try { + 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); + } + } + outw.flush(); + } catch (RevisionSyntaxException | IOException e) { + throw die(e.getMessage(), e); + } finally { + diffFmt.close(); + } + } + + 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); + } + 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)); + } + break; + default: + break; + } + } else { + break; + } + } + } + + @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; + } + } + return launchCompare; + } + + private void showToolHelp() throws IOException { + 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$ + } + 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$ + name, userTools.get(name).getCommand())); + } + outw.println(MessageFormat.format( + CLIText.get().diffToolHelpSetToFollowing, availableToolNames, + userToolNames, notAvailableToolNames)); + } + + private List<DiffEntry> getFiles() + throws RevisionSyntaxException, AmbiguousObjectException, + IncorrectObjectTypeException, IOException { + diffFmt.setRepository(db); + if (cached) { + if (oldTree == null) { + ObjectId head = db.resolve(HEAD + "^{tree}"); //$NON-NLS-1$ + if (head == null) { + die(MessageFormat.format(CLIText.get().notATree, HEAD)); + } + CanonicalTreeParser p = new CanonicalTreeParser(); + try (ObjectReader reader = db.newObjectReader()) { + p.reset(reader, head); + } + oldTree = p; + } + newTree = new DirCacheIterator(db.readDirCache()); + } else if (oldTree == null) { + oldTree = new DirCacheIterator(db.readDirCache()); + newTree = new FileTreeIterator(db); + } else if (newTree == null) { + newTree = new FileTreeIterator(db); + } + + TextProgressMonitor pm = new TextProgressMonitor(errw); + pm.setDelayStart(2, TimeUnit.SECONDS); + diffFmt.setProgressMonitor(pm); + diffFmt.setPathFilter(pathFilter); + + List<DiffEntry> files = diffFmt.scan(oldTree, newTree); + return files; + } + +} diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java index ca4877fb34..27a3d90fad 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java @@ -10,6 +10,8 @@ package org.eclipse.jgit.pgm; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH; + import java.io.IOException; import java.text.MessageFormat; import java.util.Map; @@ -144,8 +146,10 @@ class Merge extends TextBuiltin { case FAST_FORWARD: ObjectId oldHeadId = oldHead.getObjectId(); if (oldHeadId != null) { - String oldId = oldHeadId.abbreviate(7).name(); - String newId = result.getNewHead().abbreviate(7).name(); + String oldId = oldHeadId + .abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH).name(); + String newId = result.getNewHead() + .abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH).name(); outw.println(MessageFormat.format(CLIText.get().updating, oldId, newId)); } diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reflog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reflog.java index 030119ea34..c63532df60 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reflog.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reflog.java @@ -9,6 +9,8 @@ */ package org.eclipse.jgit.pgm; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH; + import java.io.IOException; import java.util.Collection; @@ -45,7 +47,8 @@ class Reflog extends TextBuiltin { private String toString(ReflogEntry entry, int i) { final StringBuilder s = new StringBuilder(); - s.append(entry.getNewId().abbreviate(7).name()); + s.append(entry.getNewId().abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH) + .name()); s.append(" "); //$NON-NLS-1$ s.append(ref == null ? Constants.HEAD : Repository.shortenRefName(ref)); s.append("@{" + i + "}:"); //$NON-NLS-1$ //$NON-NLS-2$ 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 8e49a76a33..7fe5b0fa45 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 @@ -136,6 +136,9 @@ public class CLIText extends TranslationBundle { /***/ public String dateInfo; /***/ public String deletedBranch; /***/ public String deletedRemoteBranch; + /***/ public String diffToolHelpSetToFollowing; + /***/ public String diffToolLaunch; + /***/ public String diffToolDied; /***/ public String doesNotExist; /***/ public String dontOverwriteLocalChanges; /***/ public String everythingUpToDate; diff --git a/org.eclipse.jgit.ssh.apache.agent/BUILD b/org.eclipse.jgit.ssh.apache.agent/BUILD index 0c8cf838d4..f2e4d55165 100644 --- a/org.eclipse.jgit.ssh.apache.agent/BUILD +++ b/org.eclipse.jgit.ssh.apache.agent/BUILD @@ -17,6 +17,6 @@ java_library( "//lib:slf4j-api", "//lib:sshd-osgi", "//org.eclipse.jgit:jgit", - "//org.eclipse.jgit.ssh.apache:ssh-apache" + "//org.eclipse.jgit.ssh.apache:ssh-apache", ], ) 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 3fd0f528bb..1a5746df00 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,15 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent;singleton:=true -Bundle-Version: 6.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Vendor: %Bundle-Vendor -Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[6.0.1,6.1.0)" +Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[6.1.1,6.2.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.0.1,6.1.0)", - org.eclipse.jgit.nls;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)" +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)" 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.0.1";x-internal:=true +Export-Package: org.eclipse.jgit.internal.transport.sshd.agent.connector;version="6.1.1";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 808de5359e..d06e6b172f 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.ssh.apache.agent/README.md b/org.eclipse.jgit.ssh.apache.agent/README.md new file mode 100644 index 0000000000..6d62a2fd81 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.agent/README.md @@ -0,0 +1,82 @@ +# JGit SSH agent transport support for Apache MINA sshd + +This bundle provides optional support for communicating with an SSH agent +for SSH or SFTP authentication. It is an OSGi fragment for bundle +[org.eclipse.jgit.ssh.apache](../org.eclipse.jgit.ssh.apache/README.md), +and it provides transports for local communication with SSH agents. + +## Supported SSH agent transports + +### Linux, OS X, BSD + +On Linux, OS X, and BSD, the only transport mechanism supported is the usual +communication via a Unix domain socket. This is the only protocol the OpenSSH +SSH agent supports. A Unix domain socket appears as a special file in the file +system; this file name is typically available in the environment variable +`SSH_AUTH_SOCK`. + +The SSH config `IdentityAgent` can be set to this socket filename to specify +exactly which Unix domain socket to use, or it can be set to `SSH_AUTH_SOCK` +to use the value from that environment variable. If `IdentityAgent` is not set +at all, JGit uses `SSH_AUTH_SOCK` by default. If the variable is not set, no +SSH agent will be used. `IdentityAgent` can also be set to `none` to not use +any SSH agent. + +### Windows + +On Windows, two different transports are supported: + +* A transport over a Windows named pipe. This is used by Win32-OpenSSH, and is available for Pageant since version 0.75. +* A Pageant-specific legacy transport via shared memory; useful for Pageant and GPG's gpg-agent. + +Possible settings of `IdentityAgent` to select a particular transport are + +* `//./pipe/openssh-ssh-agent`: the Windows named pipe of Win32-OpenSSH. +* `//./pageant`: the shared-memory mechanism of Pageant. +* `none`: do not use any SSH agent. +* `//./pipe/<any_valid_pipe_name>`: use a specific Windows named pipe. + +The default transport on Windows if `IdentityAgent` is not set at all is the +Pageant shared-memory transport. Environment variable `SSH_AUTH_SOCK` needs +not be set for Pageant, and _must not_ be set for Win32-OpenSSH. + +It is also possible to use a named pipe as transport for Pageant (as of +version 0.75). Unfortunately, Pageant unnecessarily cryptographically +obfuscates the pipe name, so it is not possible for JGit to determine it +automatically. The pipe name is `pageant.<user name>.<sha256>`, for instance +`pageant.myself.c5687736ba755a70b000955cb191698aed7db221c2b0710199eb1f5298922ab5`. +A user can look up the name by starting Pageant and then running the +command `dir \\.\pipe\\` in a command shell. Once the name is known, setting +`IdentityAgent` to the pipe name as +`//./pipe/pageant.myself.c5687736ba755a70b000955cb191698aed7db221c2b0710199eb1f5298922ab5` +makes JGit use this Windows named pipe for communication with Pageant. + +(You can use forward slashes in the `~/.ssh/config` file. SSH config file +parsing has its own rules about backslashes in config files; which are +treated as escape characters in some contexts. With backslashes one would +have to write, e.g., `\\\\.\pipe\openssh-ssh-agent`.) + +With these two transport mechanisms, Pageant and Win32-OpenSSH are supported. +As for GPG: the gpg-agent can be configured to emulate ssh-agent (presumably +via a WinSockets2 "Unix domain socket" on Windows) or to emulate Pageant +(using the shared memory mechanism). Running gpg-agent with the +`enable-ssh-support` option is +[reported not to work on Windows](https://dev.gnupg.org/T3883), though. But +the PuTTY emulation in gpg-agent (option `enable-putty-support`) _should_ work, +so it should be possible to use gpg-agent instead of Pageant. + +Neither Pageant (as of version 0.76) nor Win32-OpenSSH (as of version 8.6) +support the `confirm` or lifetime constraints for `AddKeysToAgent`. gpg-agent +apparently does, even when communicating over the Pageant shared memory +mechanism. + +The ssh-agent from git bash on Windows is currently not supported. It would +need a connector handling Cygwin socket files and the Cygwin handshake over +a TCP stream socket bound to the loopback interface. The Cygwin socket file +_is_ exposed in the Windows file system at %TEMP%\ssh-XXXXXXXXXX\agent.<number>, +but it does not have a fixed name (the X's and the number are variable and +change each time ssh-agent is started). + +## Implementation + +The implementation of all transports uses JNA.
\ No newline at end of file diff --git a/org.eclipse.jgit.ssh.apache.agent/pom.xml b/org.eclipse.jgit.ssh.apache.agent/pom.xml index 1bd3fad485..77b9c952fb 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ssh.apache.agent</artifactId> @@ -132,7 +132,6 @@ </configuration> </plugin> - <!-- New in 6.0; uncomment in 6.1 <plugin> <groupId>com.github.siom79.japicmp</groupId> <artifactId>japicmp-maven-plugin</artifactId> @@ -155,6 +154,9 @@ <includes> <include>org.eclipse.jgit.*</include> </includes> + <excludes> + <exclude>*.internal.*</exclude> + </excludes> <accessModifier>public</accessModifier> <breakBuildOnModifications>false</breakBuildOnModifications> <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications> @@ -174,11 +176,9 @@ </execution> </executions> </plugin> - --> </plugins> </build> - <!-- New in 6.0, uncomment in 6.1 <reporting> <plugins> <plugin> @@ -210,6 +210,9 @@ <includes> <include>org.eclipse.jgit.*</include> </includes> + <excludes> + <exclude>*.internal.*</exclude> + </excludes> <accessModifier>public</accessModifier> <breakBuildOnModifications>false</breakBuildOnModifications> <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications> @@ -223,5 +226,4 @@ </plugin> </plugins> </reporting> - --> </project> diff --git a/org.eclipse.jgit.ssh.apache.agent/resources/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.properties b/org.eclipse.jgit.ssh.apache.agent/resources/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.properties index 6fce083668..a3b4e91cad 100644 --- a/org.eclipse.jgit.ssh.apache.agent/resources/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.properties +++ b/org.eclipse.jgit.ssh.apache.agent/resources/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.properties @@ -2,9 +2,11 @@ errCloseMappedFile=Cannot close mapped file: {0} - {1} errLastError=System message for error {0} could not be retrieved, got {1} errReleaseSharedMemory=Cannot release shared memory: {0} - {1} errUnknown=unknown error +errUnknownIdentityAgent=IdentityAgent ''{0}'' unknown logErrorLoadLibrary=Cannot load socket library; SSH agent support is switched off msgCloseFailed=Cannot close SSH agent socket {0} msgConnectFailed=Could not connect to SSH agent via socket ''{0}'' +msgConnectPipeFailed=Could not connect to SSH agent via pipe ''{0}'' msgNoMappedFile=Could not create file mapping: {0} - {1} msgNoSharedMemory=Could not initialize shared memory: {0} - {1} msgPageantUnavailable=Could not connect to Pageant @@ -15,3 +17,4 @@ msgSharedMemoryFailed=Could not set up shared memory for communicating with Page msgShortRead=Short read from SSH agent, expected {0} bytes, got {1} bytes; last read() returned {2} pageant=Pageant unixDefaultAgent=ssh-agent +winOpenSsh=Win32 OpenSSH diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Factory.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Factory.java index d7409b0c3c..1cee1be13e 100644 --- a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Factory.java +++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Factory.java @@ -11,11 +11,15 @@ package org.eclipse.jgit.internal.transport.sshd.agent.connector; import java.io.File; import java.io.IOException; +import java.text.MessageFormat; import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.Locale; import org.eclipse.jgit.transport.sshd.agent.Connector; import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory; +import org.eclipse.jgit.util.StringUtils; import org.eclipse.jgit.util.SystemReader; /** @@ -29,7 +33,20 @@ public class Factory implements ConnectorFactory { public Connector create(String identityAgent, File homeDir) throws IOException { if (SystemReader.getInstance().isWindows()) { - return new PageantConnector(); + if (StringUtils.isEmptyOrNull(identityAgent)) { + // Default. + return new PageantConnector(); + } + String winPath = identityAgent.replace('/', '\\'); + if (PageantConnector.DESCRIPTOR.getIdentityAgent() + .equalsIgnoreCase(winPath)) { + return new PageantConnector(); + } + if (winPath.toLowerCase(Locale.ROOT).startsWith("\\\\.\\pipe\\")) { //$NON-NLS-1$ + return new WinPipeConnector(winPath); + } + throw new IOException(MessageFormat.format( + Texts.get().errUnknownIdentityAgent, identityAgent)); } return new UnixDomainSocketConnector(identityAgent); } @@ -55,7 +72,11 @@ public class Factory implements ConnectorFactory { */ @Override public Collection<ConnectorDescriptor> getSupportedConnectors() { - return Collections.singleton(getDefaultConnector()); + if (SystemReader.getInstance().isWindows()) { + return List.of(PageantConnector.DESCRIPTOR, + WinPipeConnector.DESCRIPTOR); + } + return Collections.singleton(UnixDomainSocketConnector.DESCRIPTOR); } @Override diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/LibraryHolder.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/LibraryHolder.java index b09b55f817..0a592d0500 100644 --- a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/LibraryHolder.java +++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/LibraryHolder.java @@ -53,6 +53,10 @@ class LibraryHolder { kernel = Kernel32.INSTANCE; } + String systemError() { + return systemError("[{0}] - {1}"); //$NON-NLS-1$ + } + String systemError(String pattern) { int lastError = kernel.GetLastError(); String msg; diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantConnector.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantConnector.java index b0e3bce724..19684ecb93 100644 --- a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantConnector.java +++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantConnector.java @@ -26,7 +26,10 @@ public class PageantConnector extends AbstractConnector { @Override public String getIdentityAgent() { - return "pageant"; //$NON-NLS-1$ + // This must be an absolute Windows path name to avoid that + // OpenSshConfigFile treats it as a relative path name. Use an UNC + // name on localhost, like for pipes. + return "\\\\.\\pageant"; //$NON-NLS-1$ } @Override diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Sockets.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Sockets.java index 3d95bdb51c..52cf5f22f2 100644 --- a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Sockets.java +++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Sockets.java @@ -24,11 +24,6 @@ public final class Sockets { } /** - * Default SSH agent socket environment variable name. - */ - public static final String ENV_SSH_AUTH_SOCK = "SSH_AUTH_SOCK"; //$NON-NLS-1$ - - /** * Domain for Unix domain sockets. */ public static final int AF_UNIX = 1; diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.java index fb45b30dd2..f387c76ad8 100644 --- a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.java +++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.java @@ -31,9 +31,11 @@ public final class Texts extends TranslationBundle { /***/ public String errLastError; /***/ public String errReleaseSharedMemory; /***/ public String errUnknown; + /***/ public String errUnknownIdentityAgent; /***/ public String logErrorLoadLibrary; /***/ public String msgCloseFailed; /***/ public String msgConnectFailed; + /***/ public String msgConnectPipeFailed; /***/ public String msgNoMappedFile; /***/ public String msgNoSharedMemory; /***/ public String msgPageantUnavailable; @@ -44,5 +46,6 @@ public final class Texts extends TranslationBundle { /***/ public String msgShortRead; /***/ public String pageant; /***/ public String unixDefaultAgent; + /***/ public String winOpenSsh; } diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixDomainSocketConnector.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixDomainSocketConnector.java index 3b75f3a7da..95ac34f940 100644 --- a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixDomainSocketConnector.java +++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixDomainSocketConnector.java @@ -11,10 +11,10 @@ package org.eclipse.jgit.internal.transport.sshd.agent.connector; import static org.eclipse.jgit.internal.transport.sshd.agent.connector.Sockets.AF_UNIX; import static org.eclipse.jgit.internal.transport.sshd.agent.connector.Sockets.DEFAULT_PROTOCOL; -import static org.eclipse.jgit.internal.transport.sshd.agent.connector.Sockets.ENV_SSH_AUTH_SOCK; import static org.eclipse.jgit.internal.transport.sshd.agent.connector.Sockets.SOCK_STREAM; import static org.eclipse.jgit.internal.transport.sshd.agent.connector.UnixSockets.FD_CLOEXEC; import static org.eclipse.jgit.internal.transport.sshd.agent.connector.UnixSockets.F_SETFD; +import static org.eclipse.jgit.transport.SshConstants.ENV_SSH_AUTH_SOCKET; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -46,7 +46,7 @@ public class UnixDomainSocketConnector extends AbstractConnector { @Override public String getIdentityAgent() { - return ENV_SSH_AUTH_SOCK; + return ENV_SSH_AUTH_SOCKET; } @Override @@ -91,8 +91,9 @@ public class UnixDomainSocketConnector extends AbstractConnector { public UnixDomainSocketConnector(String socketFile) { super(); String file = socketFile; - if (StringUtils.isEmptyOrNull(file)) { - file = SystemReader.getInstance().getenv(ENV_SSH_AUTH_SOCK); + if (StringUtils.isEmptyOrNull(file) + || ENV_SSH_AUTH_SOCKET.equals(file)) { + file = SystemReader.getInstance().getenv(ENV_SSH_AUTH_SOCKET); } this.socketFile = file; } 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 new file mode 100644 index 0000000000..7bad90f24f --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/WinPipeConnector.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.internal.transport.sshd.agent.connector; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.sshd.common.SshException; +import org.eclipse.jgit.transport.sshd.agent.AbstractConnector; +import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory.ConnectorDescriptor; +import org.eclipse.jgit.util.StringUtils; + +import com.sun.jna.LastErrorException; +import com.sun.jna.platform.win32.WinBase; +import com.sun.jna.platform.win32.WinError; +import com.sun.jna.platform.win32.WinNT; +import com.sun.jna.platform.win32.WinNT.HANDLE; +import com.sun.jna.ptr.IntByReference; + +/** + * A connector based on JNA using Windows' named pipes to communicate with an + * ssh agent. This is used by Microsoft's Win32-OpenSSH port. + */ +public class WinPipeConnector extends AbstractConnector { + + // Pipe names are, like other file names, case-insensitive on Windows. + private static final String CANONICAL_PIPE_NAME = "\\\\.\\pipe\\openssh-ssh-agent"; //$NON-NLS-1$ + + /** + * {@link ConnectorDescriptor} for the {@link PageantConnector}. + */ + public static final ConnectorDescriptor DESCRIPTOR = new ConnectorDescriptor() { + + @Override + public String getIdentityAgent() { + return CANONICAL_PIPE_NAME; + } + + @Override + public String getDisplayName() { + return Texts.get().winOpenSsh; + } + }; + + private static final int FILE_SHARE_NONE = 0; + + private static final int FILE_ATTRIBUTE_NONE = 0; + + private final String pipeName; + + private final AtomicBoolean connected = new AtomicBoolean(); + + // It's a byte pipe, so the normal Windows file mechanisms can be used. + // Would one of the standard Java File I/O abstractions work? + private volatile HANDLE fileHandle; + + /** + * Creates a {@link WinPipeConnector} for the given named pipe. + * + * @param pipeName + * to connect to + */ + public WinPipeConnector(String pipeName) { + this.pipeName = pipeName.replace('/', '\\'); + } + + @Override + public boolean connect() throws IOException { + if (StringUtils.isEmptyOrNull(pipeName)) { + return false; + } + HANDLE file = fileHandle; + synchronized (this) { + if (connected.get()) { + return true; + } + LibraryHolder libs = LibraryHolder.getLibrary(); + if (libs == null) { + return false; + } + 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) { + int errorCode = libs.kernel.GetLastError(); + if (errorCode == WinError.ERROR_FILE_NOT_FOUND + && CANONICAL_PIPE_NAME.equalsIgnoreCase(pipeName)) { + // OpenSSH agent not running. Don't throw. + return false; + } + LastErrorException cause = new LastErrorException( + libs.systemError()); + throw new IOException(MessageFormat + .format(Texts.get().msgConnectPipeFailed, pipeName), + cause); + } + connected.set(true); + } + fileHandle = file; + return connected.get(); + } + + @Override + public synchronized void close() throws IOException { + HANDLE file = fileHandle; + if (connected.getAndSet(false) && fileHandle != null) { + fileHandle = null; + LibraryHolder libs = LibraryHolder.getLibrary(); + boolean success = libs.kernel.CloseHandle(file); + if (!success) { + LastErrorException cause = new LastErrorException( + libs.systemError()); + throw new IOException(MessageFormat + .format(Texts.get().msgCloseFailed, pipeName), cause); + } + } + } + + @Override + public byte[] rpc(byte command, byte[] message) throws IOException { + prepareMessage(command, message); + HANDLE file = fileHandle; + if (!connected.get() || file == null) { + // No translation, internal error + throw new IllegalStateException("Not connected to SSH agent"); //$NON-NLS-1$ + } + LibraryHolder libs = LibraryHolder.getLibrary(); + writeFully(libs, file, message); + // Now receive the reply + byte[] lengthBuf = new byte[4]; + readFully(libs, file, lengthBuf); + int length = toLength(command, lengthBuf); + byte[] payload = new byte[length]; + readFully(libs, file, payload); + return payload; + } + + private void writeFully(LibraryHolder libs, HANDLE file, byte[] message) + throws IOException { + byte[] buf = message; + int toWrite = buf.length; + try { + while (toWrite > 0) { + IntByReference written = new IntByReference(); + boolean success = libs.kernel.WriteFile(file, buf, buf.length, + written, null); + if (!success) { + throw new LastErrorException(libs.systemError()); + } + int actuallyWritten = written.getValue(); + toWrite -= actuallyWritten; + if (actuallyWritten > 0 && toWrite > 0) { + buf = Arrays.copyOfRange(buf, actuallyWritten, buf.length); + } + } + } catch (LastErrorException e) { + throw new IOException(MessageFormat.format( + Texts.get().msgSendFailed, Integer.toString(message.length), + Integer.toString(toWrite)), e); + } + } + + private void readFully(LibraryHolder libs, HANDLE file, byte[] data) + throws IOException { + int n = 0; + int offset = 0; + while (offset < data.length && (n = read(libs, file, data, offset, + data.length - offset)) > 0) { + offset += n; + } + if (offset < data.length) { + throw new SshException(MessageFormat.format( + Texts.get().msgShortRead, Integer.toString(data.length), + Integer.toString(offset), Integer.toString(n))); + } + } + + private int read(LibraryHolder libs, HANDLE file, byte[] buffer, int offset, + int length) throws IOException { + try { + int toRead = length; + IntByReference read = new IntByReference(); + if (offset == 0) { + boolean success = libs.kernel.ReadFile(file, buffer, toRead, + read, null); + if (!success) { + throw new LastErrorException(libs.systemError()); + } + return read.getValue(); + } + byte[] data = new byte[length]; + boolean success = libs.kernel.ReadFile(file, buffer, toRead, read, + null); + if (!success) { + throw new LastErrorException(libs.systemError()); + } + int actuallyRead = read.getValue(); + if (actuallyRead > 0) { + System.arraycopy(data, 0, buffer, offset, actuallyRead); + } + return actuallyRead; + } catch (LastErrorException e) { + throw new IOException(MessageFormat.format( + Texts.get().msgReadFailed, Integer.toString(length)), e); + } + } +} 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 8ff9735f47..411d43cf8a 100644 --- a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF @@ -3,34 +3,34 @@ 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.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-11 -Import-Package: org.apache.sshd.client.config.hosts;version="[2.7.0,2.8.0)", - org.apache.sshd.common;version="[2.7.0,2.8.0)", - org.apache.sshd.common.auth;version="[2.7.0,2.8.0)", - org.apache.sshd.common.config.keys;version="[2.7.0,2.8.0)", - org.apache.sshd.common.helpers;version="[2.7.0,2.8.0)", - org.apache.sshd.common.kex;version="[2.7.0,2.8.0)", - org.apache.sshd.common.keyprovider;version="[2.7.0,2.8.0)", - org.apache.sshd.common.session;version="[2.7.0,2.8.0)", - org.apache.sshd.common.signature;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.net;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.security;version="[2.7.0,2.8.0)", - org.apache.sshd.core;version="[2.7.0,2.8.0)", - org.apache.sshd.server;version="[2.7.0,2.8.0)", - org.apache.sshd.server.forward;version="[2.7.0,2.8.0)", - org.eclipse.jgit.api;version="[6.0.1,6.1.0)", - org.eclipse.jgit.api.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.transport.sshd.proxy;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit.ssh;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.sshd;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.sshd.agent;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)", +Import-Package: org.apache.sshd.client.config.hosts;version="[2.8.0,2.9.0)", + org.apache.sshd.common;version="[2.8.0,2.9.0)", + org.apache.sshd.common.auth;version="[2.8.0,2.9.0)", + org.apache.sshd.common.config.keys;version="[2.8.0,2.9.0)", + org.apache.sshd.common.helpers;version="[2.8.0,2.9.0)", + org.apache.sshd.common.kex;version="[2.8.0,2.9.0)", + org.apache.sshd.common.keyprovider;version="[2.8.0,2.9.0)", + org.apache.sshd.common.session;version="[2.8.0,2.9.0)", + org.apache.sshd.common.signature;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.net;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.security;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.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 a88ddafc45..3e98e33371 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 ccaf98ced0..3d7c7651c1 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 @@ -107,6 +107,32 @@ public class ApacheSshTest extends SshTestBase { "IdentityFile " + privateKey1.getAbsolutePath()); } + /** + * Test for SSHD-1231. If authentication is attempted first with an RSA key, + * which is rejected, and then with some other key type (here ed25519), + * authentication fails in bug SSHD-1231. + * + * @throws Exception + * on errors + * @see <a href= + * "https://issues.apache.org/jira/browse/SSHD-1231">SSHD-1231</a> + */ + @Test + public void testWrongKeyFirst() throws Exception { + File userKey = new File(getTemporaryDirectory(), "userkey"); + copyTestResource("id_ed25519", userKey); + File publicKey = new File(getTemporaryDirectory(), "userkey.pub"); + copyTestResource("id_ed25519.pub", publicKey); + server.setTestUserPublicKey(publicKey.toPath()); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath(), // RSA + "IdentityFile " + userKey.getAbsolutePath()); + } + @Test public void testHashedKnownHosts() throws Exception { assertTrue("Failed to delete known_hosts", knownHosts.delete()); diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF index 81e01ccadc..73feaeca85 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.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 -Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.0.1";x-internal:=true; +Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.1.1";x-internal:=true; uses:="org.apache.sshd.client, org.apache.sshd.client.auth, org.apache.sshd.client.auth.keyboard, @@ -23,72 +23,75 @@ Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.0.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.0.1";x-internal:=true, - org.eclipse.jgit.internal.transport.sshd.auth;version="6.0.1";x-internal:=true, - org.eclipse.jgit.internal.transport.sshd.proxy;version="6.0.1";x-friends:="org.eclipse.jgit.ssh.apache.test", - org.eclipse.jgit.transport.sshd;version="6.0.1"; + 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"; 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.0.1" + org.eclipse.jgit.transport.sshd.agent;version="6.1.1" Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)", - org.apache.sshd.agent;version="[2.7.0,2.8.0)", - org.apache.sshd.client;version="[2.7.0,2.8.0)", - org.apache.sshd.client.auth;version="[2.7.0,2.8.0)", - org.apache.sshd.client.auth.keyboard;version="[2.7.0,2.8.0)", - org.apache.sshd.client.auth.password;version="[2.7.0,2.8.0)", - org.apache.sshd.client.auth.pubkey;version="[2.7.0,2.8.0)", - org.apache.sshd.client.channel;version="[2.7.0,2.8.0)", - org.apache.sshd.client.config.hosts;version="[2.7.0,2.8.0)", - org.apache.sshd.client.config.keys;version="[2.7.0,2.8.0)", - org.apache.sshd.client.future;version="[2.7.0,2.8.0)", - org.apache.sshd.client.keyverifier;version="[2.7.0,2.8.0)", - org.apache.sshd.client.session;version="[2.7.0,2.8.0)", - org.apache.sshd.client.session.forward;version="[2.7.0,2.8.0)", - org.apache.sshd.common;version="[2.7.0,2.8.0)", - org.apache.sshd.common.auth;version="[2.7.0,2.8.0)", - org.apache.sshd.common.channel;version="[2.7.0,2.8.0)", - org.apache.sshd.common.compression;version="[2.7.0,2.8.0)", - org.apache.sshd.common.config.keys;version="[2.7.0,2.8.0)", - org.apache.sshd.common.config.keys.loader;version="[2.7.0,2.8.0)", - org.apache.sshd.common.config.keys.loader.openssh.kdf;version="[2.7.0,2.8.0)", - org.apache.sshd.common.digest;version="[2.7.0,2.8.0)", - org.apache.sshd.common.forward;version="[2.7.0,2.8.0)", - org.apache.sshd.common.future;version="[2.7.0,2.8.0)", - org.apache.sshd.common.helpers;version="[2.7.0,2.8.0)", - org.apache.sshd.common.io;version="[2.7.0,2.8.0)", - org.apache.sshd.common.kex;version="[2.7.0,2.8.0)", - org.apache.sshd.common.kex.extension;version="[2.7.0,2.8.0)", - org.apache.sshd.common.kex.extension.parser;version="[2.7.0,2.8.0)", - org.apache.sshd.common.keyprovider;version="[2.7.0,2.8.0)", - org.apache.sshd.common.mac;version="[2.7.0,2.8.0)", - org.apache.sshd.common.random;version="[2.7.0,2.8.0)", - org.apache.sshd.common.session;version="[2.7.0,2.8.0)", - org.apache.sshd.common.session.helpers;version="[2.7.0,2.8.0)", - org.apache.sshd.common.signature;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.buffer;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.closeable;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.io;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.io.functors;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.io.resource;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.logging;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.net;version="[2.7.0,2.8.0)", - org.apache.sshd.common.util.security;version="[2.7.0,2.8.0)", - org.apache.sshd.core;version="[2.7.0,2.8.0)", - org.apache.sshd.server.auth;version="[2.7.0,2.8.0)", - org.apache.sshd.sftp;version="[2.7.0,2.8.0)", - org.apache.sshd.sftp.client;version="[2.7.0,2.8.0)", - org.apache.sshd.sftp.common;version="[2.7.0,2.8.0)", - org.eclipse.jgit.annotations;version="[6.0.1,6.1.0)", - org.eclipse.jgit.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.fnmatch;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.transport.ssh;version="[6.0.1,6.1.0)", - org.eclipse.jgit.nls;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)", + org.apache.sshd.agent;version="[2.8.0,2.9.0)", + org.apache.sshd.client;version="[2.8.0,2.9.0)", + org.apache.sshd.client.auth;version="[2.8.0,2.9.0)", + org.apache.sshd.client.auth.keyboard;version="[2.8.0,2.9.0)", + org.apache.sshd.client.auth.password;version="[2.8.0,2.9.0)", + org.apache.sshd.client.auth.pubkey;version="[2.8.0,2.9.0)", + org.apache.sshd.client.channel;version="[2.8.0,2.9.0)", + org.apache.sshd.client.config.hosts;version="[2.8.0,2.9.0)", + org.apache.sshd.client.config.keys;version="[2.8.0,2.9.0)", + org.apache.sshd.client.future;version="[2.8.0,2.9.0)", + org.apache.sshd.client.keyverifier;version="[2.8.0,2.9.0)", + org.apache.sshd.client.session;version="[2.8.0,2.9.0)", + org.apache.sshd.client.session.forward;version="[2.8.0,2.9.0)", + org.apache.sshd.common;version="[2.8.0,2.9.0)", + org.apache.sshd.common.auth;version="[2.8.0,2.9.0)", + org.apache.sshd.common.channel;version="[2.8.0,2.9.0)", + org.apache.sshd.common.compression;version="[2.8.0,2.9.0)", + org.apache.sshd.common.config.keys;version="[2.8.0,2.9.0)", + org.apache.sshd.common.config.keys.loader;version="[2.8.0,2.9.0)", + org.apache.sshd.common.config.keys.loader.openssh.kdf;version="[2.8.0,2.9.0)", + org.apache.sshd.common.config.keys.u2f;version="[2.8.0,2.9.0)", + org.apache.sshd.common.digest;version="[2.8.0,2.9.0)", + org.apache.sshd.common.forward;version="[2.8.0,2.9.0)", + org.apache.sshd.common.future;version="[2.8.0,2.9.0)", + org.apache.sshd.common.helpers;version="[2.8.0,2.9.0)", + org.apache.sshd.common.io;version="[2.8.0,2.9.0)", + org.apache.sshd.common.kex;version="[2.8.0,2.9.0)", + org.apache.sshd.common.kex.extension;version="[2.8.0,2.9.0)", + org.apache.sshd.common.kex.extension.parser;version="[2.8.0,2.9.0)", + org.apache.sshd.common.keyprovider;version="[2.8.0,2.9.0)", + org.apache.sshd.common.mac;version="[2.8.0,2.9.0)", + org.apache.sshd.common.random;version="[2.8.0,2.9.0)", + org.apache.sshd.common.session;version="[2.8.0,2.9.0)", + org.apache.sshd.common.session.helpers;version="[2.8.0,2.9.0)", + org.apache.sshd.common.signature;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.buffer;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.buffer.keys;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.closeable;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.io;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.io.der;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.io.functors;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.io.resource;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.logging;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.net;version="[2.8.0,2.9.0)", + org.apache.sshd.common.util.security;version="[2.8.0,2.9.0)", + org.apache.sshd.core;version="[2.8.0,2.9.0)", + org.apache.sshd.server.auth;version="[2.8.0,2.9.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.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 b770b90344..9fefa6e74c 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.ssh.apache/README.md b/org.eclipse.jgit.ssh.apache/README.md index cba87ac9cc..f06b2f6071 100644 --- a/org.eclipse.jgit.ssh.apache/README.md +++ b/org.eclipse.jgit.ssh.apache/README.md @@ -43,6 +43,53 @@ FetchCommand fetch = git.fetch() .call(); ``` +## Support for SSH agents + +There exist two IETF draft RFCs for communication with an SSH agent: + +* an older [SSH1 protocol](https://tools.ietf.org/html/draft-ietf-secsh-agent-02) that can deal only with DSA and RSA keys, and +* a newer [SSH2 protocol](https://tools.ietf.org/html/draft-miller-ssh-agent-04) (from OpenSSH). + +JGit only supports the newer OpenSSH protocol. + +Communication with an SSH agent can occur over any transport protocol, and different +SSH agents may use different transports for local communication. JGit provides some +transports via the [org.eclipse.jgit.ssh.apache.agent](../org.eclipse.jgit.ssh.apache.agent/README.md) +fragment, which are discovered from `org.eclipse.jgit.ssh.apache` also via the `ServiceLoader` mechanism; +the SPI (service provider interface) is `org.eclipse.jgit.transport.sshd.agent.ConnectorFactory`. + +If such a `ConnectorFactory` implementation is found, JGit may use an SSH agent. If none +is available, JGit cannot communicate with an SSH agent, and will not attempt to use one. + +### SSH configurations for SSH agents + +There are several SSH properties that can be used in the `~/.ssh/config` file to configure +the use of an SSH agent. For the details, see the [OpenBSD ssh-config documentation](https://man.openbsd.org/ssh_config.5). + +* **AddKeysToAgent** can be set to `no`, `yes`, or `ask`. If set to `yes`, keys will be added + to the agent if they're not yet in the agent. If set to `ask`, the user will be prompted + before doing so, and can opt out of adding the key. JGit also supports the additional + settings `confirm` and key lifetimes. +* **IdentityAgent** can be set to choose which SSH agent to use, if there are several running. + It can also be set to `none` to explicitly switch off using an SSH agent at all. +* **IdentitiesOnly** if set to `yes` and an SSH agent is used, only keys from the agent that are + also listed in an `IdentityFile` property will be considered. (It'll also switch off trying + default key names, such as `~/.ssh/id_rsa` or `~/.ssh/id_ed25519`; only keys listed explicitly + will be used.) + +### Limitations + +As mentioned above JGit only implements the newer OpenSSH protocol. OpenSSH fully implements this, +but some other SSH agents only offer partial implementations. In particular on Windows, neither +Pageant nor Win32-OpenSSH implement the `confirm` or lifetime constraints for `AddKeysToAgent`. With +such SSH agents, these settings should not be used in `~/.ssh/config`. GPG's gpg-agent can be run +with option `enable_putty_support` and can then be used as a Pageant replacement. gpg-agent appears +to support these key constraints. + +OpenSSH does not implement ed448 keys, and neither does Apache MINA sshd, and hence such keys are +not supported in JGit if its built-in SSH implementation is used. ed448 or other unsupported keys +provided by an SSH agent are ignored. + ## Using a different SSH implementation To use a different SSH implementation: diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml index 340cca158d..674c7a49e5 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ssh.apache</artifactId> @@ -50,6 +50,16 @@ <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> @@ -154,6 +164,9 @@ <includes> <include>org.eclipse.jgit.*</include> </includes> + <excludes> + <exclude>*.internal.*</exclude> + </excludes> <accessModifier>public</accessModifier> <breakBuildOnModifications>false</breakBuildOnModifications> <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications> @@ -207,6 +220,9 @@ <includes> <include>org.eclipse.jgit.*</include> </includes> + <excludes> + <exclude>*.internal.*</exclude> + </excludes> <accessModifier>public</accessModifier> <breakBuildOnModifications>false</breakBuildOnModifications> <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications> 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 2bba736aad..4f735bab34 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,6 @@ authenticationCanceled=SSH authentication canceled: no password given authenticationOnClosedSession=Authentication canceled: session is already closing or closed +cannotReadPublicKey=Cannot read public key from file {0} closeListenerFailed=Ssh session close listener failed configInvalidPath=Invalid path in ssh config key {0}: {1} configInvalidPattern=Invalid pattern in ssh config key {0}: {1} @@ -77,6 +78,8 @@ proxySocksPasswordTooLong=Password for proxy {0} must be at most 255 bytes long, proxySocksUnexpectedMessage=Unexpected message received from SOCKS5 proxy {0}; client state {1}: {2} proxySocksUnexpectedVersion=Expected SOCKS version 5, got {0} proxySocksUsernameTooLong=User name for proxy {0} must be at most 255 bytes long, is {1} bytes: {2} +pubkeyAuthAddKeyToAgentError=Could not add {0} key with fingerprint {1} to the SSH agent +pubkeyAuthAddKeyToAgentQuestion=Add the {0} key with fingerprint {1} to the SSH agent? pubkeyAuthWrongCommand=Public key authentication received unknown SSH command {0} from {1} ({2}) pubkeyAuthWrongKey=Public key authentication received wrong key; sent {0}, got back {1} from {2} ({3}) pubkeyAuthWrongSignatureAlgorithm=Public key authentication requested signature type {0} but got back {1} from {2} ({3}) @@ -85,9 +88,13 @@ serverIdTooLong=Server identification is longer than 255 characters (including l serverIdWithNul=Server identification contains a NUL character: {0} sessionCloseFailed=Closing the session failed sessionWithoutUsername=SSH session created without user name; cannot authenticate +sshAgentEdDSAFormatError=Cannot add ed25519 key to the SSH agent because it is encoded as {0} instead of PKCS#8 +sshAgentPayloadLengthError=Expected {0,choice,0#no bytes|1#one byte|1<{0} bytes} but got {1} sshAgentReplyLengthError=Invalid SSH agent reply message length {0} after command {1} sshAgentReplyUnexpected=Unexpected reply from ssh-agent: {0} sshAgentShortReadBuffer=Short read from SSH agent +sshAgentUnknownKey=SSH agent delivered a key that cannot be handled +sshAgentWrongKeyLength=SSH agent delivered illogical key length {0} at offset {1} in buffer of length {2} sshAgentWrongNumberOfKeys=Invalid number of SSH agent keys: {0} sshClosingDown=Apache MINA sshd session factory is closing down; cannot create new ssh sessions on this factory sshCommandTimeout={0} timed out after {1} seconds while opening the channel diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java index e2dbb4c466..5100bc9e54 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java @@ -42,7 +42,6 @@ import org.apache.sshd.common.io.IoSession; import org.apache.sshd.common.io.IoWriteFuture; import org.apache.sshd.common.kex.BuiltinDHFactories; import org.apache.sshd.common.kex.DHFactory; -import org.apache.sshd.common.kex.KexProposalOption; import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.kex.extension.KexExtensionHandler; import org.apache.sshd.common.kex.extension.KexExtensionHandler.AvailabilityPhase; @@ -199,24 +198,6 @@ public class JGitClientSession extends ClientSessionImpl { } } - @Override - protected Map<KexProposalOption, String> setNegotiationResult( - Map<KexProposalOption, String> guess) { - Map<KexProposalOption, String> result = super.setNegotiationResult( - guess); - // This should be doable with a SessionListener, too, but I don't see - // how to add a listener in time to catch the negotiation end for sure - // given that the super-constructor already starts KEX. - // - // TODO: This override can be removed once we use sshd 2.8.0. - if (log.isDebugEnabled()) { - result.forEach((option, value) -> log.debug( - "setNegotiationResult({}) Kex: {} = {}", this, //$NON-NLS-1$ - option.getDescription(), value)); - } - return result; - } - Set<String> getAllAvailableSignatureAlgorithms() { Set<String> allAvailable = new HashSet<>(); BuiltinSignatures.VALUES.forEach(s -> allAvailable.add(s.getName())); diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java index c082a9a963..96da0cccdd 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java @@ -12,25 +12,68 @@ package org.eclipse.jgit.internal.transport.sshd; import static java.text.MessageFormat.format; import static org.eclipse.jgit.transport.SshConstants.PUBKEY_ACCEPTED_ALGORITHMS; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.stream.Collectors; +import org.apache.sshd.agent.SshAgent; +import org.apache.sshd.agent.SshAgentFactory; +import org.apache.sshd.agent.SshAgentKeyConstraint; import org.apache.sshd.client.auth.pubkey.KeyAgentIdentity; import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity; import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey; +import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator; import org.apache.sshd.client.config.hosts.HostConfigEntry; import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.FactoryManager; import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; +import org.apache.sshd.common.config.keys.KeyUtils; +import org.apache.sshd.common.config.keys.PublicKeyEntryResolver; +import org.apache.sshd.common.config.keys.u2f.SecurityKeyPublicKey; import org.apache.sshd.common.signature.Signature; +import org.apache.sshd.common.signature.SignatureFactoriesManager; +import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile; +import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.SshConstants; +import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.util.StringUtils; /** * Custom {@link UserAuthPublicKey} implementation for handling SSH config - * PubkeyAcceptedAlgorithms. + * PubkeyAcceptedAlgorithms and interaction with the SSH agent. */ public class JGitPublicKeyAuthentication extends UserAuthPublicKey { + private SshAgent agent; + + private HostConfigEntry hostConfig; + + private boolean addKeysToAgent; + + private boolean askBeforeAdding; + + private String skProvider; + + private SshAgentKeyConstraint[] constraints; + JGitPublicKeyAuthentication(List<NamedFactory<Signature>> factories) { super(factories); } @@ -43,7 +86,7 @@ public class JGitPublicKeyAuthentication extends UserAuthPublicKey { + rawSession.getClass().getCanonicalName()); } JGitClientSession session = (JGitClientSession) rawSession; - HostConfigEntry hostConfig = session.getHostConfigEntry(); + hostConfig = session.getHostConfigEntry(); // Set signature algorithms for public key authentication String pubkeyAlgos = hostConfig.getProperty(PUBKEY_ACCEPTED_ALGORITHMS); if (!StringUtils.isEmptyOrNull(pubkeyAlgos)) { @@ -64,46 +107,291 @@ public class JGitPublicKeyAuthentication extends UserAuthPublicKey { // If we don't set signature factories here, the default ones from the // session will be used. super.init(session, service); - // In sshd 2.7.0, we end up now with a key iterator that uses keys - // provided by an ssh-agent even if IdentitiesOnly is true. So if - // needed, filter out any KeyAgentIdentity. - if (hostConfig.isIdentitiesOnly()) { - Iterator<PublicKeyIdentity> original = keys; - // The original iterator will already have gotten the identities - // from the agent. Unfortunately there's nothing we can do about - // that; it'll have to be fixed upstream. (As will, ultimately, - // respecting isIdentitiesOnly().) At least we can simply not - // use the keys the agent provided. - // - // See https://issues.apache.org/jira/browse/SSHD-1218 - keys = new Iterator<>() { - - private PublicKeyIdentity value; + } + + @Override + protected Iterator<PublicKeyIdentity> createPublicKeyIterator( + ClientSession session, SignatureFactoriesManager manager) + throws Exception { + agent = getAgent(session); + if (agent != null) { + parseAddKeys(hostConfig); + if (addKeysToAgent) { + skProvider = hostConfig.getProperty(SshConstants.SECURITY_KEY_PROVIDER); + } + } + return new KeyIterator(session, manager); + } + + @Override + protected PublicKeyIdentity resolveAttemptedPublicKeyIdentity( + ClientSession session, String service) throws Exception { + PublicKeyIdentity result = getNextKey(session, service); + // This fixes SSHD-1231. Can be removed once we're using Apache MINA + // sshd > 2.8.0. + // + // See https://issues.apache.org/jira/browse/SSHD-1231 + currentAlgorithms.clear(); + return result; + } + + private PublicKeyIdentity getNextKey(ClientSession session, String service) + throws Exception { + PublicKeyIdentity id = super.resolveAttemptedPublicKeyIdentity(session, + service); + if (addKeysToAgent && id != null && !(id instanceof KeyAgentIdentity)) { + KeyPair key = id.getKeyIdentity(); + if (key != null && key.getPublic() != null + && key.getPrivate() != null) { + // We've just successfully loaded a key that wasn't in the + // agent. Add it to the agent. + // + // Keys are added after loading, as in OpenSSH. The alternative + // might be to add a key only after (partially) successful + // authentication? + PublicKey pk = key.getPublic(); + String fingerprint = KeyUtils.getFingerPrint(pk); + String keyType = KeyUtils.getKeyType(key); + try { + // Check that the key is not in the agent already. + if (agentHasKey(pk)) { + return id; + } + if (askBeforeAdding + && (session instanceof JGitClientSession)) { + CredentialsProvider provider = ((JGitClientSession) session) + .getCredentialsProvider(); + CredentialItem.YesNoType question = new CredentialItem.YesNoType( + format(SshdText + .get().pubkeyAuthAddKeyToAgentQuestion, + keyType, fingerprint)); + boolean result = provider != null + && provider.supports(question) + && provider.get(getUri(), question); + if (!result || !question.getValue()) { + // Don't add the key. + return id; + } + } + SshAgentKeyConstraint[] rules = constraints; + if (pk instanceof SecurityKeyPublicKey && !StringUtils.isEmptyOrNull(skProvider)) { + rules = Arrays.copyOf(rules, rules.length + 1); + rules[rules.length - 1] = + new SshAgentKeyConstraint.FidoProviderExtension(skProvider); + } + // Unfortunately a comment associated with the key is lost + // by Apache MINA sshd, and there is also no way to get the + // original file name for keys loaded from a file. So add it + // without comment. + agent.addIdentity(key, null, rules); + } catch (IOException e) { + // Do not re-throw: we don't want authentication to fail if + // we cannot add the key to the agent. + log.error( + format(SshdText.get().pubkeyAuthAddKeyToAgentError, + keyType, fingerprint), + e); + // Note that as of Win32-OpenSSH 8.6 and Pageant 0.76, + // neither can handle key constraints. Pageant fails + // gracefully, not adding the key and returning + // SSH_AGENT_FAILURE. Win32-OpenSSH closes the connection + // without even returning a failure message, which violates + // the SSH agent protocol and makes all subsequent requests + // to the agent fail. + } + } + } + return id; + } + + private boolean agentHasKey(PublicKey pk) throws IOException { + Iterable<? extends Map.Entry<PublicKey, String>> ids = agent + .getIdentities(); + if (ids == null) { + return false; + } + Iterator<? extends Map.Entry<PublicKey, String>> iter = ids.iterator(); + while (iter.hasNext()) { + if (KeyUtils.compareKeys(iter.next().getKey(), pk)) { + return true; + } + } + return false; + } + + private URIish getUri() { + String uri = SshConstants.SSH_SCHEME + "://"; //$NON-NLS-1$ + String userName = hostConfig.getUsername(); + if (!StringUtils.isEmptyOrNull(userName)) { + uri += userName + '@'; + } + uri += hostConfig.getHost(); + int port = hostConfig.getPort(); + if (port > 0 && port != SshConstants.SSH_DEFAULT_PORT) { + uri += ":" + port; //$NON-NLS-1$ + } + try { + return new URIish(uri); + } catch (URISyntaxException e) { + log.error(e.getLocalizedMessage(), e); + } + return new URIish(); + } + + private SshAgent getAgent(ClientSession session) throws Exception { + FactoryManager manager = Objects.requireNonNull( + session.getFactoryManager(), "No session factory manager"); //$NON-NLS-1$ + SshAgentFactory factory = manager.getAgentFactory(); + if (factory == null) { + return null; + } + return factory.createClient(session, manager); + } + + private void parseAddKeys(HostConfigEntry config) { + String value = config.getProperty(SshConstants.ADD_KEYS_TO_AGENT); + if (StringUtils.isEmptyOrNull(value)) { + addKeysToAgent = false; + return; + } + String[] values = value.split(","); //$NON-NLS-1$ + List<SshAgentKeyConstraint> rules = new ArrayList<>(2); + switch (values[0]) { + case "yes": //$NON-NLS-1$ + addKeysToAgent = true; + break; + case "no": //$NON-NLS-1$ + addKeysToAgent = false; + break; + case "ask": //$NON-NLS-1$ + addKeysToAgent = true; + askBeforeAdding = true; + break; + case "confirm": //$NON-NLS-1$ + addKeysToAgent = true; + rules.add(SshAgentKeyConstraint.CONFIRM); + if (values.length > 1) { + int seconds = OpenSshConfigFile.timeSpec(values[1]); + if (seconds > 0) { + rules.add(new SshAgentKeyConstraint.LifeTime(seconds)); + } + } + break; + default: + int seconds = OpenSshConfigFile.timeSpec(values[0]); + if (seconds > 0) { + addKeysToAgent = true; + rules.add(new SshAgentKeyConstraint.LifeTime(seconds)); + } + break; + } + constraints = rules.toArray(new SshAgentKeyConstraint[0]); + } + + @Override + protected void releaseKeys() throws IOException { + addKeysToAgent = false; + askBeforeAdding = false; + skProvider = null; + constraints = null; + try { + if (agent != null) { + try { + agent.close(); + } finally { + agent = null; + } + } + } finally { + super.releaseKeys(); + } + } + + private class KeyIterator extends UserAuthPublicKeyIterator { + + private Iterable<? extends Map.Entry<PublicKey, String>> agentKeys; + + // If non-null, all the public keys from explicitly given key files. Any + // agent key not matching one of these public keys will be ignored in + // getIdentities(). + private Collection<PublicKey> identityFiles; + + public KeyIterator(ClientSession session, + SignatureFactoriesManager manager) + throws Exception { + super(session, manager); + } + + private List<PublicKey> getExplicitKeys( + Collection<String> explicitFiles) { + if (explicitFiles == null) { + return null; + } + return explicitFiles.stream().map(s -> { + try { + Path p = Paths.get(s + ".pub"); //$NON-NLS-1$ + if (Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS)) { + return AuthorizedKeyEntry.readAuthorizedKeys(p).get(0) + .resolvePublicKey(null, + PublicKeyEntryResolver.IGNORING); + } + } catch (InvalidPathException | IOException + | GeneralSecurityException e) { + log.warn(format(SshdText.get().cannotReadPublicKey, s), e); + } + return null; + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + + @Override + protected Iterable<KeyAgentIdentity> initializeAgentIdentities( + ClientSession session) throws IOException { + if (agent == null) { + return null; + } + agentKeys = agent.getIdentities(); + if (hostConfig != null && hostConfig.isIdentitiesOnly()) { + identityFiles = getExplicitKeys(hostConfig.getIdentities()); + } + return () -> new Iterator<>() { + + private final Iterator<? extends Map.Entry<PublicKey, String>> iter = agentKeys + .iterator(); + + private Map.Entry<PublicKey, String> next; @Override public boolean hasNext() { - if (value != null) { - return true; - } - PublicKeyIdentity next = null; - while (original.hasNext()) { - next = original.next(); - if (!(next instanceof KeyAgentIdentity)) { - value = next; + while (next == null && iter.hasNext()) { + Map.Entry<PublicKey, String> val = iter.next(); + PublicKey pk = val.getKey(); + // This checks against all explicit keys for any agent + // key, but since identityFiles.size() is typically 1, + // it should be fine. + if (identityFiles == null || identityFiles.stream() + .anyMatch(k -> KeyUtils.compareKeys(k, pk))) { + next = val; return true; } + if (log.isTraceEnabled()) { + log.trace( + "Ignoring SSH agent {} key not in explicit IdentityFile in SSH config: {}", //$NON-NLS-1$ + KeyUtils.getKeyType(pk), + KeyUtils.getFingerPrint(pk)); + } } - return false; + return next != null; } @Override - public PublicKeyIdentity next() { - if (hasNext()) { - PublicKeyIdentity result = value; - value = null; - return result; + public KeyAgentIdentity next() { + if (!hasNext()) { + throw new NoSuchElementException(); } - throw new NoSuchElementException(); + KeyAgentIdentity result = new KeyAgentIdentity(agent, + next.getKey(), next.getValue()); + next = null; + return result; } }; } 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 00ee62d6dd..19ad85c83d 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 @@ -30,6 +30,7 @@ public final class SshdText extends TranslationBundle { /***/ public String authenticationCanceled; /***/ public String authenticationOnClosedSession; /***/ public String closeListenerFailed; + /***/ public String cannotReadPublicKey; /***/ public String configInvalidPath; /***/ public String configInvalidPattern; /***/ public String configInvalidPositive; @@ -98,6 +99,8 @@ public final class SshdText extends TranslationBundle { /***/ public String proxySocksUnexpectedMessage; /***/ public String proxySocksUnexpectedVersion; /***/ public String proxySocksUsernameTooLong; + /***/ public String pubkeyAuthAddKeyToAgentError; + /***/ public String pubkeyAuthAddKeyToAgentQuestion; /***/ public String pubkeyAuthWrongCommand; /***/ public String pubkeyAuthWrongKey; /***/ public String pubkeyAuthWrongSignatureAlgorithm; @@ -106,9 +109,13 @@ public final class SshdText extends TranslationBundle { /***/ public String serverIdWithNul; /***/ public String sessionCloseFailed; /***/ public String sessionWithoutUsername; + /***/ public String sshAgentEdDSAFormatError; + /***/ public String sshAgentPayloadLengthError; /***/ public String sshAgentReplyLengthError; /***/ public String sshAgentReplyUnexpected; /***/ public String sshAgentShortReadBuffer; + /***/ public String sshAgentUnknownKey; + /***/ public String sshAgentWrongKeyLength; /***/ public String sshAgentWrongNumberOfKeys; /***/ public String sshClosingDown; /***/ public String sshCommandTimeout; diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/JGitSshAgentFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/JGitSshAgentFactory.java index 1ed2ab9d78..a0ffd540f2 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/JGitSshAgentFactory.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/JGitSshAgentFactory.java @@ -17,10 +17,14 @@ import java.util.List; import org.apache.sshd.agent.SshAgent; import org.apache.sshd.agent.SshAgentFactory; import org.apache.sshd.agent.SshAgentServer; +import org.apache.sshd.client.config.hosts.HostConfigEntry; import org.apache.sshd.common.FactoryManager; import org.apache.sshd.common.channel.ChannelFactory; import org.apache.sshd.common.session.ConnectionService; +import org.apache.sshd.common.session.Session; import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.internal.transport.sshd.JGitClientSession; +import org.eclipse.jgit.transport.SshConstants; import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory; /** @@ -49,18 +53,24 @@ public class JGitSshAgentFactory implements SshAgentFactory { @Override public List<ChannelFactory> getChannelForwardingFactories( FactoryManager manager) { - // No agent forwarding supported yet. + // No agent forwarding supported. return Collections.emptyList(); } @Override - public SshAgent createClient(FactoryManager manager) throws IOException { - // sshd 2.8.0 will pass us the session here. At that point, we can get - // the HostConfigEntry and extract and handle the IdentityAgent setting. - // For now, pass null to let the ConnectorFactory do its default - // behavior (Pageant on Windows, SSH_AUTH_SOCK on Unixes with the - // jgit-builtin factory). - return new SshAgentClient(factory.create(null, homeDir)); + public SshAgent createClient(Session session, FactoryManager manager) + throws IOException { + String identityAgent = null; + if (session instanceof JGitClientSession) { + HostConfigEntry hostConfig = ((JGitClientSession) session) + .getHostConfigEntry(); + identityAgent = hostConfig.getProperty(SshConstants.IDENTITY_AGENT, + null); + } + if (SshConstants.NONE.equals(identityAgent)) { + return null; + } + return new SshAgentClient(factory.create(identityAgent, homeDir)); } @Override diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/SshAgentClient.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/SshAgentClient.java index 08483e4c20..cbcb4d240e 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/SshAgentClient.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/SshAgentClient.java @@ -11,10 +11,12 @@ package org.eclipse.jgit.internal.transport.sshd.agent; import java.io.IOException; import java.security.KeyPair; +import java.security.PrivateKey; import java.security.PublicKey; import java.text.MessageFormat; import java.util.AbstractMap; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -22,21 +24,27 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.apache.sshd.agent.SshAgent; import org.apache.sshd.agent.SshAgentConstants; +import org.apache.sshd.agent.SshAgentKeyConstraint; import org.apache.sshd.common.SshException; import org.apache.sshd.common.config.keys.KeyUtils; +import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.session.SessionContext; import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.buffer.BufferException; import org.apache.sshd.common.util.buffer.BufferUtils; import org.apache.sshd.common.util.buffer.ByteArrayBuffer; +import org.apache.sshd.common.util.buffer.keys.BufferPublicKeyParser; +import org.apache.sshd.common.util.io.der.DERParser; import org.eclipse.jgit.internal.transport.sshd.SshdText; import org.eclipse.jgit.transport.sshd.agent.Connector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * A client for an SSH2 agent. This client supports only querying identities and - * signature requests. + * A client for an SSH2 agent. This client supports querying identities, + * signature requests, and adding keys to an agent (with or without + * constraints). Removing keys is not supported, and the older SSH1 protocol is + * not supported. * * @see <a href="https://tools.ietf.org/html/draft-miller-ssh-agent-04">SSH * Agent Protocol, RFC draft</a> @@ -72,11 +80,18 @@ public class SshAgentClient implements SshAgent { } return false; } - boolean connected = connector != null && connector.connect(); - if (!connected) { + boolean connected; + try { + connected = connector != null && connector.connect(); + if (!connected && debugging) { + LOG.debug("No SSH agent"); //$NON-NLS-1$ + } + } catch (IOException e) { + // Agent not running? if (debugging) { - LOG.debug("No SSH agent (SSH_AUTH_SOCK not set)"); //$NON-NLS-1$ + LOG.debug("No SSH agent", e); //$NON-NLS-1$ } + throw e; } return connected; } @@ -127,14 +142,17 @@ public class SshAgentClient implements SshAgent { List<Map.Entry<PublicKey, String>> keys = new ArrayList<>( numberOfKeys); for (int i = 0; i < numberOfKeys; i++) { - PublicKey key = reply.getPublicKey(); + PublicKey key = readKey(reply); String comment = reply.getString(); - if (tracing) { - LOG.trace("Got SSH agent {} key: {} {}", //$NON-NLS-1$ - KeyUtils.getKeyType(key), - KeyUtils.getFingerPrint(key), comment); + if (key != null) { + if (tracing) { + LOG.trace("Got SSH agent {} key: {} {}", //$NON-NLS-1$ + KeyUtils.getKeyType(key), + KeyUtils.getFingerPrint(key), comment); + } + keys.add(new AbstractMap.SimpleImmutableEntry<>(key, + comment)); } - keys.add(new AbstractMap.SimpleImmutableEntry<>(key, comment)); } return keys; } catch (BufferException e) { @@ -216,6 +234,222 @@ public class SshAgentClient implements SshAgent { } } + @Override + public void addIdentity(KeyPair key, String comment, + SshAgentKeyConstraint... constraints) throws IOException { + boolean debugging = LOG.isDebugEnabled(); + if (!open(debugging)) { + return; + } + + // Neither Pageant 0.76 nor Win32-OpenSSH 8.6 support command + // SSH2_AGENTC_ADD_ID_CONSTRAINED. Adding a key with constraints will + // fail. The only work-around for users is not to use "confirm" or "time + // spec" with AddKeysToAgent, and not to use sk-* keys. + // + // With a true OpenSSH SSH agent, key constraints work. + byte cmd = (constraints != null && constraints.length > 0) + ? SshAgentConstants.SSH2_AGENTC_ADD_ID_CONSTRAINED + : SshAgentConstants.SSH2_AGENTC_ADD_IDENTITY; + byte[] message = null; + ByteArrayBuffer msg = new ByteArrayBuffer(); + try { + msg.putInt(0); + msg.putByte(cmd); + String keyType = KeyUtils.getKeyType(key); + if (KeyPairProvider.SSH_ED25519.equals(keyType)) { + // Apache MINA sshd 2.8.0 lacks support for writing ed25519 + // private keys to a buffer. + putEd25519Key(msg, key); + } else { + msg.putKeyPair(key); + } + msg.putString(comment == null ? "" : comment); //$NON-NLS-1$ + if (constraints != null) { + for (SshAgentKeyConstraint constraint : constraints) { + constraint.put(msg); + } + } + if (debugging) { + LOG.debug( + "addIdentity: adding {} key {} to SSH agent; comment {}", //$NON-NLS-1$ + keyType, KeyUtils.getFingerPrint(key.getPublic()), + comment); + } + message = msg.getCompactData(); + } finally { + // The message contains the private key data, so clear intermediary + // data ASAP. + msg.clear(); + } + Buffer reply; + try { + reply = rpc(cmd, message); + } finally { + Arrays.fill(message, (byte) 0); + } + int replyLength = reply.available(); + if (replyLength != 1) { + throw new SshException(MessageFormat.format( + SshdText.get().sshAgentReplyUnexpected, + MessageFormat.format( + SshdText.get().sshAgentPayloadLengthError, + Integer.valueOf(1), Integer.valueOf(replyLength)))); + + } + cmd = reply.getByte(); + if (cmd != SshAgentConstants.SSH_AGENT_SUCCESS) { + throw new SshException( + MessageFormat.format(SshdText.get().sshAgentReplyUnexpected, + SshAgentConstants.getCommandMessageName(cmd))); + } + } + + /** + * Writes an ed25519 {@link KeyPair} to a {@link Buffer}. OpenSSH specifies + * that it expects the 32 public key bytes, followed by 64 bytes formed by + * concatenating the 32 private key bytes with the 32 public key bytes. + * + * @param msg + * {@link Buffer} to write to + * @param key + * {@link KeyPair} to write + * @throws IOException + * if the private key cannot be written + */ + private static void putEd25519Key(Buffer msg, KeyPair key) + throws IOException { + Buffer tmp = new ByteArrayBuffer(36); + tmp.putRawPublicKeyBytes(key.getPublic()); + byte[] publicBytes = tmp.getBytes(); + msg.putString(KeyPairProvider.SSH_ED25519); + msg.putBytes(publicBytes); + // Next is the concatenation of the 32 byte private key value with the + // 32 bytes of the public key. + PrivateKey pk = key.getPrivate(); + String format = pk.getFormat(); + if (!"PKCS#8".equalsIgnoreCase(format)) { //$NON-NLS-1$ + throw new IOException(MessageFormat + .format(SshdText.get().sshAgentEdDSAFormatError, format)); + } + byte[] privateBytes = null; + byte[] encoded = pk.getEncoded(); + try { + privateBytes = asn1Parse(encoded, 32); + byte[] combined = Arrays.copyOf(privateBytes, 64); + Arrays.fill(privateBytes, (byte) 0); + privateBytes = combined; + System.arraycopy(publicBytes, 0, privateBytes, 32, 32); + msg.putBytes(privateBytes); + } finally { + if (privateBytes != null) { + Arrays.fill(privateBytes, (byte) 0); + } + Arrays.fill(encoded, (byte) 0); + } + } + + /** + * Extracts the private key bytes from an encoded ed25519 private key by + * parsing the bytes as ASN.1 according to RFC 5958 (PKCS #8 encoding): + * + * <pre> + * OneAsymmetricKey ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey PrivateKey, + * ... + * } + * + * Version ::= INTEGER + * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier + * PrivateKey ::= OCTET STRING + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + * </pre> + * <p> + * and RFC 8410: "... when encoding a OneAsymmetricKey object, the private + * key is wrapped in a CurvePrivateKey object and wrapped by the OCTET + * STRING of the 'privateKey' field." + * </p> + * + * <pre> + * CurvePrivateKey ::= OCTET STRING + * </pre> + * + * @param encoded + * encoded private key to extract the private key bytes from + * @param n + * number of bytes expected + * @return the extracted private key bytes; of length {@code n} + * @throws IOException + * if the private key cannot be extracted + * @see <a href="https://tools.ietf.org/html/rfc5958">RFC 5958</a> + * @see <a href="https://tools.ietf.org/html/rfc8410">RFC 8410</a> + */ + private static byte[] asn1Parse(byte[] encoded, int n) throws IOException { + byte[] privateKey = null; + try (DERParser byteParser = new DERParser(encoded); + DERParser oneAsymmetricKey = byteParser.readObject() + .createParser()) { + oneAsymmetricKey.readObject(); // skip version + oneAsymmetricKey.readObject(); // skip algorithm identifier + privateKey = oneAsymmetricKey.readObject().getValue(); + // The last n bytes of this must be the private key bytes + return Arrays.copyOfRange(privateKey, + privateKey.length - n, privateKey.length); + } finally { + if (privateKey != null) { + Arrays.fill(privateKey, (byte) 0); + } + } + } + + /** + * A safe version of {@link Buffer#getPublicKey()}. Upon return the + * buffers's read position is always after the key blob; any exceptions + * thrown by trying to read the key are logged and <em>not</em> propagated. + * <p> + * This is needed because an SSH agent might contain and deliver keys that + * we cannot handle (for instance ed448 keys). + * </p> + * + * @param buffer + * to read the key from + * @return the {@link PublicKey}, or {@code null} if the key could not be + * read + * @throws BufferException + * if the length of the key blob cannot be read or is corrupted + */ + private static PublicKey readKey(Buffer buffer) throws BufferException { + int endOfBuffer = buffer.wpos(); + int keyLength = buffer.getInt(); + int afterKey = buffer.rpos() + keyLength; + if (keyLength <= 0 || afterKey > endOfBuffer) { + throw new BufferException( + MessageFormat.format(SshdText.get().sshAgentWrongKeyLength, + Integer.toString(keyLength), + Integer.toString(buffer.rpos()), + Integer.toString(endOfBuffer))); + } + // Limit subsequent reads to the public key blob + buffer.wpos(afterKey); + try { + return buffer.getRawPublicKey(BufferPublicKeyParser.DEFAULT); + } catch (Exception e) { + LOG.warn(SshdText.get().sshAgentUnknownKey, e); + return null; + } finally { + // Restore real buffer end + buffer.wpos(endOfBuffer); + // Set the read position to after this key, even if failed + buffer.rpos(afterKey); + } + } + private Buffer rpc(byte command, byte[] message) throws IOException { return new ByteArrayBuffer(connector.rpc(command, message)); } @@ -230,11 +464,6 @@ public class SshAgentClient implements SshAgent { } @Override - public void addIdentity(KeyPair key, String comment) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override public void removeIdentity(PublicKey key) throws IOException { throw new UnsupportedOperationException(); } 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 c270b44956..b742f5ea42 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 @@ -51,6 +51,7 @@ import org.apache.sshd.sftp.client.SftpClientFactory; import org.apache.sshd.sftp.common.SftpException; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.errors.TransportException; +import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile; import org.eclipse.jgit.internal.transport.sshd.JGitSshClient; import org.eclipse.jgit.internal.transport.sshd.SshdText; import org.eclipse.jgit.transport.FtpChannel; @@ -138,7 +139,11 @@ public class SshdSession implements RemoteSession2 { JGitSshClient.LOCAL_FORWARD_ADDRESS, portForward.getBoundAddress()); } - resultSession = connect(hostConfig, context, timeout); + int timeoutInSec = OpenSshConfigFile.timeSpec( + hostConfig.getProperty(SshConstants.CONNECT_TIMEOUT)); + resultSession = connect(hostConfig, context, + timeoutInSec > 0 ? Duration.ofSeconds(timeoutInSec) + : timeout); if (proxySession != null) { final PortForwardingTracker tracker = portForward; final ClientSession pSession = proxySession; 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 52e69bd6e2..4a08940c83 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.0.1.qualifier +Bundle-Version: 6.1.1.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.0.1,6.1.0)", - org.eclipse.jgit.junit;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit.ssh;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.ssh.jsch;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.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.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 6f199e679b..ca1e742344 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 ed997fb5cd..332d981cf9 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.0.1,6.1.0)" +Fragment-Host: org.eclipse.jgit;bundle-version="[6.1.1,6.2.0)" Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Bundle-ActivationPolicy: lazy -Bundle-Version: 6.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 -Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="6.0.1" +Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="6.1.1" Import-Package: com.jcraft.jsch;version="[0.1.37,0.2.0)", - org.eclipse.jgit.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.transport.ssh;version="[6.0.1,6.1.0)", - org.eclipse.jgit.nls;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util.io;version="[6.0.1,6.1.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.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 3c3f2b153e..b56bb7c6e0 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.ssh.jsch/pom.xml b/org.eclipse.jgit.ssh.jsch/pom.xml index 2a5f9b8dfe..3b6d66c68c 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ssh.jsch</artifactId> diff --git a/org.eclipse.jgit.test/BUILD b/org.eclipse.jgit.test/BUILD index 9e787fee71..e63c02c03c 100644 --- a/org.eclipse.jgit.test/BUILD +++ b/org.eclipse.jgit.test/BUILD @@ -13,6 +13,7 @@ HELPERS = glob( ) + [PKG + c for c in [ "api/AbstractRemoteCommandTest.java", "diff/AbstractDiffTestCase.java", + "internal/diffmergetool/ExternalToolTestCase.java", "internal/revwalk/ObjectReachabilityTestCase.java", "internal/revwalk/ReachabilityCheckerTestCase.java", "internal/storage/file/GcTestCase.java", @@ -50,6 +51,23 @@ tests(tests = glob( exclude = HELPERS + DATA + EXCLUDED, )) +# Non abstract base classes used for tests by other test classes +BASE = [ + PKG + "internal/storage/file/FileRepositoryBuilderTest.java", + PKG + "internal/storage/file/RefDirectoryTest.java", +] + +java_library( + name = "base", + testonly = 1, + srcs = BASE, + deps = [ + "//lib:junit", + "//org.eclipse.jgit:jgit", + "//org.eclipse.jgit.junit:junit", + ], +) + java_library( name = "helpers", testonly = 1, diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index 0469113443..581395d90c 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.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor Bundle-RequiredExecutionEnvironment: JavaSE-11 @@ -16,60 +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.0.1,6.1.0)", - org.eclipse.jgit.api;version="[6.0.1,6.1.0)", - org.eclipse.jgit.api.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.archive;version="[6.0.1,6.1.0)", - org.eclipse.jgit.attributes;version="[6.0.1,6.1.0)", - org.eclipse.jgit.awtui;version="[6.0.1,6.1.0)", - org.eclipse.jgit.blame;version="[6.0.1,6.1.0)", - org.eclipse.jgit.diff;version="[6.0.1,6.1.0)", - org.eclipse.jgit.dircache;version="[6.0.1,6.1.0)", - org.eclipse.jgit.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.events;version="[6.0.1,6.1.0)", - org.eclipse.jgit.fnmatch;version="[6.0.1,6.1.0)", - org.eclipse.jgit.gitrepo;version="[6.0.1,6.1.0)", - org.eclipse.jgit.hooks;version="[6.0.1,6.1.0)", - org.eclipse.jgit.ignore;version="[6.0.1,6.1.0)", - org.eclipse.jgit.ignore.internal;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.fsck;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.dfs;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.io;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.pack;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.storage.reftable;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.transport.connectivity;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.transport.http;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.transport.parser;version="[6.0.1,6.1.0)", - org.eclipse.jgit.internal.transport.ssh;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit;version="[6.0.1,6.1.0)", - org.eclipse.jgit.junit.time;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lfs;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib.internal;version="[6.0.1,6.1.0)", - org.eclipse.jgit.logging;version="[6.0.1,6.1.0)", - org.eclipse.jgit.merge;version="[6.0.1,6.1.0)", - org.eclipse.jgit.nls;version="[6.0.1,6.1.0)", - org.eclipse.jgit.notes;version="[6.0.1,6.1.0)", - org.eclipse.jgit.patch;version="[6.0.1,6.1.0)", - org.eclipse.jgit.pgm;version="[6.0.1,6.1.0)", - org.eclipse.jgit.pgm.internal;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revplot;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk.filter;version="[6.0.1,6.1.0)", - org.eclipse.jgit.storage.file;version="[6.0.1,6.1.0)", - org.eclipse.jgit.storage.pack;version="[6.0.1,6.1.0)", - org.eclipse.jgit.submodule;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.http;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport.resolver;version="[6.0.1,6.1.0)", - org.eclipse.jgit.treewalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.treewalk.filter;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util.io;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util.sha1;version="[6.0.1,6.1.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.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 3cab362f3e..6b66919894 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.test</artifactId> diff --git a/org.eclipse.jgit.test/tests.bzl b/org.eclipse.jgit.test/tests.bzl index e201bdbcb3..170bf0c460 100644 --- a/org.eclipse.jgit.test/tests.bzl +++ b/org.eclipse.jgit.test/tests.bzl @@ -52,6 +52,12 @@ def tests(tests): "//lib:xz", "//org.eclipse.jgit.archive:jgit-archive", ] + if src.endswith("FileRepositoryBuilderAfterOpenConfigTest.java") or \ + src.endswith("RefDirectoryAfterOpenConfigTest.java") or \ + src.endswith("SnapshottingRefDirectoryTest.java"): + additional_deps = [ + ":base", + ] heap_size = "-Xmx256m" if src.endswith("HugeCommitMessageTest.java"): heap_size = "-Xmx512m" diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java index 867310b60c..584d149631 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java @@ -32,6 +32,7 @@ import org.eclipse.jgit.diff.RawText; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.IO; import org.junit.Test; @@ -402,7 +403,9 @@ public class ApplyCommandTest extends RepositoryTestCase { public void testAddM1() throws Exception { ApplyResult result = init("M1", false, true); assertEquals(1, result.getUpdatedFiles().size()); - assertTrue(result.getUpdatedFiles().get(0).canExecute()); + if (FS.DETECTED.supportsExecute()) { + assertTrue(FS.DETECTED.canExecute(result.getUpdatedFiles().get(0))); + } checkFile(new File(db.getWorkTree(), "M1"), b.getString(0, b.size(), false)); } @@ -411,7 +414,9 @@ public class ApplyCommandTest extends RepositoryTestCase { public void testModifyM2() throws Exception { ApplyResult result = init("M2", true, true); assertEquals(1, result.getUpdatedFiles().size()); - assertTrue(result.getUpdatedFiles().get(0).canExecute()); + if (FS.DETECTED.supportsExecute()) { + assertTrue(FS.DETECTED.canExecute(result.getUpdatedFiles().get(0))); + } checkFile(new File(db.getWorkTree(), "M2"), b.getString(0, b.size(), false)); } @@ -420,7 +425,10 @@ public class ApplyCommandTest extends RepositoryTestCase { public void testModifyM3() throws Exception { ApplyResult result = init("M3", true, true); assertEquals(1, result.getUpdatedFiles().size()); - assertFalse(result.getUpdatedFiles().get(0).canExecute()); + if (FS.DETECTED.supportsExecute()) { + assertFalse( + FS.DETECTED.canExecute(result.getUpdatedFiles().get(0))); + } checkFile(new File(db.getWorkTree(), "M3"), b.getString(0, b.size(), false)); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java index e7ed102f13..87be813c85 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java @@ -71,6 +71,7 @@ public class BranchCommandTest extends RepositoryTestCase { private Git setUpRepoWithRemote() throws Exception { Repository remoteRepository = createWorkRepository(); + addRepoToClose(remoteRepository); try (Git remoteGit = new Git(remoteRepository)) { // commit something writeTrashFile("Test.txt", "Hello world"); @@ -85,6 +86,7 @@ public class BranchCommandTest extends RepositoryTestCase { rup.forceUpdate(); Repository localRepository = createWorkRepository(); + addRepoToClose(localRepository); Git localGit = new Git(localRepository); StoredConfig config = localRepository.getConfig(); RemoteConfig rc = new RemoteConfig(config, "origin"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java index e520732513..ab0184bd4a 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java @@ -292,6 +292,7 @@ public class CheckoutCommandTest extends RepositoryTestCase { @Test public void testCheckoutRemoteTrackingWithUpstream() throws Exception { Repository db2 = createRepositoryWithRemote(); + addRepoToClose(db2); Git.wrap(db2).checkout().setCreateBranch(true).setName("test") .setStartPoint("origin/test") @@ -311,6 +312,7 @@ public class CheckoutCommandTest extends RepositoryTestCase { @Test public void testCheckoutRemoteTrackingWithoutLocalBranch() throws Exception { Repository db2 = createRepositoryWithRemote(); + addRepoToClose(db2); // checkout remote tracking branch in second repository // (no local branches exist yet in second repository) @@ -868,7 +870,7 @@ public class CheckoutCommandTest extends RepositoryTestCase { coCommand.setName(crudCommit.getName()).call(); CheckoutResult result = coCommand.getResult(); assertEquals(Status.NONDELETED, result.getStatus()); - assertEquals("[Test.txt, toBeDeleted.txt]", + assertEquals("[toBeDeleted.txt]", result.getRemovedList().toString()); assertEquals("[toBeCreated.txt, toBeModified.txt]", result.getModifiedList().toString()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java index f4f0ecd689..0d38197d9a 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java @@ -175,7 +175,8 @@ public class CherryPickCommandTest extends RepositoryTestCase { assertEquals(CherryPickStatus.CONFLICTING, result.getStatus()); assertTrue(new File(db.getDirectory(), Constants.MERGE_MSG).exists()); - assertEquals("side\n\nConflicts:\n\ta\n", db.readMergeCommitMsg()); + assertEquals("side\n\n# Conflicts:\n#\ta\n", + db.readMergeCommitMsg()); assertTrue(new File(db.getDirectory(), Constants.CHERRY_PICK_HEAD) .exists()); assertEquals(sideCommit.getId(), db.readCherryPickHead()); @@ -207,7 +208,7 @@ public class CherryPickCommandTest extends RepositoryTestCase { String expected = "<<<<<<< master\na(master)\n=======\na(side)\n>>>>>>> 527460a side\n"; assertEquals(expected, read("a")); assertTrue(new File(db.getDirectory(), Constants.MERGE_MSG).exists()); - assertEquals("side\n\nConflicts:\n\ta\n", db.readMergeCommitMsg()); + assertEquals("side\n\n# Conflicts:\n#\ta\n", db.readMergeCommitMsg()); assertFalse(new File(db.getDirectory(), Constants.CHERRY_PICK_HEAD) .exists()); assertEquals(RepositoryState.SAFE, db.getRepositoryState()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java index 8084505c10..35de73e204 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java @@ -46,6 +46,7 @@ import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.ReflogEntry; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.lib.CommitConfig.CleanupMode; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.submodule.SubmoduleWalk; @@ -515,6 +516,62 @@ public class CommitCommandTest extends RepositoryTestCase { } @Test + public void commitMessageVerbatim() throws Exception { + try (Git git = new Git(db)) { + writeTrashFile("file1", "file1"); + git.add().addFilepattern("file1").call(); + RevCommit committed = git.commit().setMessage("#initial commit") + .call(); + + assertEquals("#initial commit", committed.getFullMessage()); + } + } + + @Test + public void commitMessageStrip() throws Exception { + try (Git git = new Git(db)) { + writeTrashFile("file1", "file1"); + git.add().addFilepattern("file1").call(); + RevCommit committed = git.commit().setMessage( + "#Comment\ninitial commit\t\n\n commit body \n \t#another comment") + .setCleanupMode(CleanupMode.STRIP).call(); + + assertEquals("initial commit\n\n commit body", + committed.getFullMessage()); + } + } + + @Test + public void commitMessageDefault() throws Exception { + try (Git git = new Git(db)) { + writeTrashFile("file1", "file1"); + git.add().addFilepattern("file1").call(); + RevCommit committed = git.commit().setMessage( + "#Comment\ninitial commit\t\n\n commit body \n\n\n \t#another comment ") + .setCleanupMode(CleanupMode.DEFAULT).call(); + + assertEquals("initial commit\n\n commit body", + committed.getFullMessage()); + } + } + + @Test + public void commitMessageDefaultWhitespace() throws Exception { + try (Git git = new Git(db)) { + writeTrashFile("file1", "file1"); + git.add().addFilepattern("file1").call(); + RevCommit committed = git.commit().setMessage( + "#Comment\ninitial commit\t\n\n commit body \n\n\n \t#another comment ") + .setCleanupMode(CleanupMode.DEFAULT).setDefaultClean(false) + .call(); + + assertEquals( + "#Comment\ninitial commit\n\n commit body\n\n \t#another comment", + committed.getFullMessage()); + } + } + + @Test public void commitEmptyCommits() throws Exception { try (Git git = new Git(db)) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java index b460e3f52e..ab87fa9662 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java @@ -10,9 +10,10 @@ package org.eclipse.jgit.api; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.BufferedWriter; @@ -100,6 +101,12 @@ public class DescribeCommandTest extends RepositoryTestCase { assertEquals("alice-t1-2-g3e563c5", describe(c4, "alice*")); assertEquals("bob-t2-1-g3e563c5", describe(c4, "bob*")); assertEquals("bob-t2-1-g3e563c5", describe(c4, "a*", "b*", "c*")); + + assertEquals("bob-t2", describe(c4, false, true, 0)); + assertEquals("bob-t2-1-g3e56", describe(c4, false, true, 1)); + assertEquals("bob-t2-1-g3e56", describe(c4, false, true, -10)); + assertEquals("bob-t2-1-g3e563c55927905f21e3bc7c00a3d83a31bf4ed3a", + describe(c4, false, true, 50)); } else { assertEquals(null, describe(c2)); assertEquals(null, describe(c3)); @@ -108,6 +115,18 @@ public class DescribeCommandTest extends RepositoryTestCase { assertEquals("3747db3", describe(c2, false, true)); assertEquals("44579eb", describe(c3, false, true)); assertEquals("3e563c5", describe(c4, false, true)); + + assertEquals("3747db3267", describe(c2, false, true, 10)); + assertEquals("44579ebe7f", describe(c3, false, true, 10)); + assertEquals("3e563c5592", describe(c4, false, true, 10)); + + assertEquals("3e56", describe(c4, false, true, -10)); + assertEquals("3e56", describe(c4, false, true, 0)); + assertEquals("3e56", describe(c4, false, true, 2)); + assertEquals("3e563c55927905f21e3bc7c00a3d83a31bf4ed3a", + describe(c4, false, true, 40)); + assertEquals("3e563c55927905f21e3bc7c00a3d83a31bf4ed3a", + describe(c4, false, true, 42)); } // test default target @@ -474,10 +493,15 @@ public class DescribeCommandTest extends RepositoryTestCase { } } + private String describe(ObjectId c1, boolean longDesc, boolean always, + int abbrev) throws GitAPIException, IOException { + return git.describe().setTarget(c1).setTags(describeUseAllTags) + .setLong(longDesc).setAlways(always).setAbbrev(abbrev).call(); + } + private String describe(ObjectId c1, boolean longDesc, boolean always) throws GitAPIException, IOException { - return git.describe().setTarget(c1).setTags(describeUseAllTags) - .setLong(longDesc).setAlways(always).call(); + return describe(c1, longDesc, always, OBJECT_ID_ABBREV_STRING_LENGTH); } private String describe(ObjectId c1) throws GitAPIException, IOException { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java index d0dfd1ab92..b937b1f6a9 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java @@ -1,40 +1,11 @@ /* - * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> + * Copyright (C) 2015, 2022 Ivan Motsch <ivan.motsch@bsiag.com> and others * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php + * 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. * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.api; @@ -173,15 +144,22 @@ public class EolRepositoryTest extends RepositoryTestCase { private DirCache dirCache; + private boolean isDefaultCrLf() { + String eol = mockSystemReader.getProperty("line.separator"); + return "\r\n".equals(eol); + } + @Test public void testDefaultSetup() throws Exception { // for EOL to work, the text attribute must be set setupGitAndDoHardReset(null, null, null, null, "* text=auto"); collectRepositoryState(); assertEquals("text=auto", entryCRLF.attrs); - checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); - checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); - checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + // eol=native is the default! + String expected = isDefaultCrLf() ? CONTENT_CRLF : CONTENT_LF; + checkEntryContent(entryCRLF, expected, CONTENT_LF); + checkEntryContent(entryLF, expected, CONTENT_LF); + checkEntryContent(entryMixed, expected, CONTENT_LF); } public void checkEntryContent(ActualEntry entry, String fileContent, @@ -199,9 +177,11 @@ public class EolRepositoryTest extends RepositoryTestCase { setupGitAndDoHardReset(AutoCRLF.FALSE, null, null, null, "* text=auto"); collectRepositoryState(); assertEquals("text=auto", entryCRLF.attrs); - checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); - checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); - checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + // eol=native is the default! + String expected = isDefaultCrLf() ? CONTENT_CRLF : CONTENT_LF; + checkEntryContent(entryCRLF, expected, CONTENT_LF); + checkEntryContent(entryLF, expected, CONTENT_LF); + checkEntryContent(entryMixed, expected, CONTENT_LF); } @Test @@ -250,34 +230,24 @@ public class EolRepositoryTest extends RepositoryTestCase { @Test public void test_ConfigEOL_native_windows() throws Exception { - String origLineSeparator = System.getProperty("line.separator", "\n"); - System.setProperty("line.separator", "\r\n"); - try { - // for EOL to work, the text attribute must be set - setupGitAndDoHardReset(null, EOL.NATIVE, "*.txt text", null, null); - collectRepositoryState(); - assertEquals("text", entryCRLF.attrs); - checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); - checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); - } finally { - System.setProperty("line.separator", origLineSeparator); - } + mockSystemReader.setWindows(); + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(null, EOL.NATIVE, "*.txt text", null, null); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); } @Test public void test_ConfigEOL_native_xnix() throws Exception { - String origLineSeparator = System.getProperty("line.separator", "\n"); - System.setProperty("line.separator", "\n"); - try { - // for EOL to work, the text attribute must be set - setupGitAndDoHardReset(null, EOL.NATIVE, "*.txt text", null, null); - collectRepositoryState(); - assertEquals("text", entryCRLF.attrs); - checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); - checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); - } finally { - System.setProperty("line.separator", origLineSeparator); - } + mockSystemReader.setUnix(); + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(null, EOL.NATIVE, "*.txt text", null, null); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); } @Test @@ -297,9 +267,10 @@ public class EolRepositoryTest extends RepositoryTestCase { setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.NATIVE, "*.txt text", null, null); collectRepositoryState(); assertEquals("text", entryCRLF.attrs); - checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); - checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); - checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + String expected = isDefaultCrLf() ? CONTENT_CRLF : CONTENT_LF; + checkEntryContent(entryCRLF, expected, CONTENT_LF); + checkEntryContent(entryLF, expected, CONTENT_LF); + checkEntryContent(entryMixed, expected, CONTENT_LF); } @Test @@ -524,9 +495,12 @@ public class EolRepositoryTest extends RepositoryTestCase { // info overrides all collectRepositoryState(); assertEquals("text", entryCRLF.attrs); - checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); - checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); - checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + // !eol means unspecified, so use the default of core.eol, which is + // native. + String expected = isDefaultCrLf() ? CONTENT_CRLF : CONTENT_LF; + checkEntryContent(entryCRLF, expected, CONTENT_LF); + checkEntryContent(entryLF, expected, CONTENT_LF); + checkEntryContent(entryMixed, expected, CONTENT_LF); } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchAndPullCommandsRecurseSubmodulesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchAndPullCommandsRecurseSubmodulesTest.java index 36ba7ce5ac..9ea64efc64 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchAndPullCommandsRecurseSubmodulesTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchAndPullCommandsRecurseSubmodulesTest.java @@ -259,7 +259,8 @@ public class FetchAndPullCommandsRecurseSubmodulesTest extends RepositoryTestCas // the commit that was created try (SubmoduleWalk w = SubmoduleWalk.forIndex(git.getRepository())) { assertTrue(w.next()); - try (Git g = new Git(w.getRepository())) { + try (Repository repository = w.getRepository(); + Git g = new Git(repository)) { g.fetch().setRemote(REMOTE).setRefSpecs(REFSPEC).call(); g.reset().setMode(ResetType.HARD).setRef(commit1.name()).call(); } 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 bc4e9405ea..64475f5d50 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 @@ -554,7 +554,7 @@ public class MergeCommandTest extends RepositoryTestCase { git.merge().include(sideBranch) .setStrategy(MergeStrategy.RESOLVE).call(); - assertEquals("Merge branch 'side'\n\nConflicts:\n\ta\n", + assertEquals("Merge branch 'side'\n\n# Conflicts:\n#\ta\n", db.readMergeCommitMsg()); } @@ -1787,7 +1787,7 @@ public class MergeCommandTest extends RepositoryTestCase { + dateFormatter.formatDate(third .getAuthorIdent()) + "\n\n\tthird commit\n", db.readSquashCommitMsg()); - assertEquals("\nConflicts:\n\tfile2\n", db.readMergeCommitMsg()); + assertEquals("\n# Conflicts:\n#\tfile2\n", db.readMergeCommitMsg()); Status stat = git.status().call(); assertEquals(Sets.of("file2"), stat.getConflicting()); @@ -1881,6 +1881,7 @@ public class MergeCommandTest extends RepositoryTestCase { @Test public void testRecursiveMergeWithConflict() throws Exception { try (TestRepository<Repository> db_t = new TestRepository<>(db)) { + db.incrementOpen(); BranchBuilder master = db_t.branch("master"); RevCommit m0 = master.commit() .add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9\n").message("m0") @@ -2012,7 +2013,7 @@ public class MergeCommandTest extends RepositoryTestCase { git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE) .setMessage("user message").call(); - assertEquals("user message\n\nConflicts:\n\ta\n", + assertEquals("user message\n\n# Conflicts:\n#\ta\n", db.readMergeCommitMsg()); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java index 9af77aa3e8..6a84f0a38d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java @@ -567,6 +567,7 @@ public class PullCommandTest extends RepositoryTestCase { public void setUp() throws Exception { super.setUp(); dbTarget = createWorkRepository(); + addRepoToClose(dbTarget); source = new Git(db); target = new Git(dbTarget); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java index cce04f471b..e2d7923cee 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java @@ -323,6 +323,7 @@ public class PullCommandWithRebaseTest extends RepositoryTestCase { dbTarget = createWorkRepository(); source = new Git(db); target = new Git(dbTarget); + addRepoToClose(dbTarget); // put some file in the source repo sourceFile = new File(db.getWorkTree(), "SomeFile.txt"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java index a786065561..6f7aa63edc 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java @@ -10,7 +10,9 @@ package org.eclipse.jgit.api; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -19,10 +21,13 @@ import java.io.IOException; import java.net.URISyntaxException; import java.util.Properties; +import org.eclipse.jgit.api.errors.DetachedHeadException; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.InvalidRefNameException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.TransportException; import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.errors.NoRemoteRepositoryException; import org.eclipse.jgit.hooks.PrePushHook; import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; @@ -32,6 +37,7 @@ import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.transport.PushConfig.PushDefault; import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.RefLeaseSpec; import org.eclipse.jgit.transport.RefSpec; @@ -50,6 +56,7 @@ public class PushCommandTest extends RepositoryTestCase { // create other repository Repository db2 = createWorkRepository(); + addRepoToClose(db2); final StoredConfig config2 = db2.getConfig(); // this tests that this config can be parsed properly @@ -289,6 +296,770 @@ public class PushCommandTest extends RepositoryTestCase { } /** + * Check that pushing from a detached HEAD without refspec throws a + * DetachedHeadException. + * + * @throws Exception + */ + @Test + public void testPushDefaultDetachedHead() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + final StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + RevCommit commit = git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + git.checkout().setName(commit.getName()).call(); + String head = git.getRepository().getFullBranch(); + assertTrue(ObjectId.isId(head)); + assertEquals(commit.getName(), head); + assertThrows(DetachedHeadException.class, + () -> git.push().setRemote("test").call()); + } + } + + /** + * Check that push.default=nothing without refspec throws an + * InvalidRefNameException. + * + * @throws Exception + */ + @Test + public void testPushDefaultNothing() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + final StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + assertEquals(null, + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + assertThrows(InvalidRefNameException.class, + () -> git.push().setRemote("test") + .setPushDefault(PushDefault.NOTHING).call()); + } + } + + /** + * Check that push.default=matching without refspec pushes all matching + * branches. + * + * @throws Exception + */ + @Test + public void testPushDefaultMatching() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + final StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + RevCommit commit = git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + assertEquals(null, + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + // push master and branchtopush + git.push().setRemote("test").setRefSpecs( + new RefSpec("refs/heads/master:refs/heads/master"), + new RefSpec( + "refs/heads/branchtopush:refs/heads/branchtopush")) + .call(); + assertEquals(commit.getId(), + git2.getRepository().resolve("refs/heads/master")); + assertEquals(commit.getId(), + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + // Create two different commits on these two branches + writeTrashFile("b", "on branchtopush"); + git.add().addFilepattern("b").call(); + RevCommit bCommit = git.commit().setMessage("on branchtopush") + .call(); + git.checkout().setName("master").call(); + writeTrashFile("m", "on master"); + git.add().addFilepattern("m").call(); + RevCommit mCommit = git.commit().setMessage("on master").call(); + // Now push with mode "matching": should push both branches. + Iterable<PushResult> result = git.push().setRemote("test") + .setPushDefault(PushDefault.MATCHING) + .call(); + int n = 0; + for (PushResult r : result) { + n++; + assertEquals(1, n); + assertEquals(2, r.getRemoteUpdates().size()); + for (RemoteRefUpdate update : r.getRemoteUpdates()) { + assertFalse(update.isMatching()); + assertTrue(update.getSrcRef() + .equals("refs/heads/branchtopush") + || update.getSrcRef().equals("refs/heads/master")); + assertEquals(RemoteRefUpdate.Status.OK, update.getStatus()); + } + } + assertEquals(bCommit.getId(), + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(mCommit.getId(), + git2.getRepository().resolve("refs/heads/master")); + assertEquals(bCommit.getId(), git.getRepository() + .resolve("refs/remotes/origin/branchtopush")); + assertEquals(null, git.getRepository() + .resolve("refs/remotes/origin/not-pushed")); + assertEquals(mCommit.getId(), + git.getRepository().resolve("refs/remotes/origin/master")); + } + } + + /** + * Check that push.default=upstream without refspec pushes only the current + * branch to the configured upstream. + * + * @throws Exception + */ + @Test + public void testPushDefaultUpstream() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + RevCommit commit = git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + config.setString("branch", "branchtopush", "remote", "test"); + config.setString("branch", "branchtopush", "merge", + "refs/heads/upstreambranch"); + config.save(); + + assertEquals(null, + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/upstreambranch")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + git.push().setRemote("test").setPushDefault(PushDefault.UPSTREAM) + .call(); + assertEquals(null, + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(commit.getId(), + git2.getRepository().resolve("refs/heads/upstreambranch")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + assertEquals(commit.getId(), git.getRepository() + .resolve("refs/remotes/origin/upstreambranch")); + assertEquals(null, git.getRepository() + .resolve("refs/remotes/origin/branchtopush")); + } + } + + /** + * Check that push.default=upstream without refspec throws an + * InvalidRefNameException if the current branch has no upstream. + * + * @throws Exception + */ + @Test + public void testPushDefaultUpstreamNoTracking() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + config.setString("branch", "branchtopush", "remote", "test"); + config.save(); + + assertThrows(InvalidRefNameException.class, + () -> git.push().setRemote("test") + .setPushDefault(PushDefault.UPSTREAM).call()); + } + } + + /** + * Check that push.default=upstream without refspec throws an + * InvalidRefNameException if the push remote is not the same as the fetch + * remote. + * + * @throws Exception + */ + @Test + public void testPushDefaultUpstreamTriangular() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + // Don't configure a remote; it'll default to "origin". + config.setString("branch", "branchtopush", "merge", + "upstreambranch"); + config.save(); + + assertThrows(InvalidRefNameException.class, + () -> git.push().setRemote("test") + .setPushDefault(PushDefault.UPSTREAM).call()); + } + } + + /** + * Check that push.default=simple without refspec pushes only the current + * branch to the configured upstream name. + * + * @throws Exception + */ + @Test + public void testPushDefaultSimple() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + RevCommit commit = git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + config.setString("branch", "branchtopush", "remote", "test"); + config.setString("branch", "branchtopush", "merge", + "refs/heads/branchtopush"); + config.save(); + + assertEquals(null, + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + git.push().setRemote("test").setPushDefault(PushDefault.SIMPLE) + .call(); + assertEquals(commit.getId(), + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + assertEquals(commit.getId(), git.getRepository() + .resolve("refs/remotes/origin/branchtopush")); + } + } + + /** + * Check that push.default=simple without refspec pushes only the current + * branch to a branch with the same name in a triangular workflow. + * + * @throws Exception + */ + @Test + public void testPushDefaultSimpleTriangular() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + RevCommit commit = git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + // Don't set remote, it'll default to "origin". Configure a + // different branch name; should be ignored. + config.setString("branch", "branchtopush", "merge", + "refs/heads/upstreambranch"); + config.save(); + + assertEquals(null, + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/upstreambranch")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + git.push().setRemote("test").setPushDefault(PushDefault.SIMPLE) + .call(); + assertEquals(commit.getId(), + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/upstreambranch")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + assertEquals(commit.getId(), git.getRepository() + .resolve("refs/remotes/origin/branchtopush")); + } + } + + /** + * Check that push.default=simple without refspec throws an + * InvalidRefNameException if the current branch has no upstream. + * + * @throws Exception + */ + @Test + public void testPushDefaultSimpleNoTracking() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + config.setString("branch", "branchtopush", "remote", "test"); + config.save(); + + assertThrows(InvalidRefNameException.class, + () -> git.push().setRemote("test") + .setPushDefault(PushDefault.SIMPLE).call()); + } + } + + /** + * Check that push.default=simple without refspec throws an + * InvalidRefNameException if the current branch has an upstream with a + * different name. + * + * @throws Exception + */ + @Test + public void testPushDefaultSimpleDifferentTracking() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + config.setString("branch", "branchtopush", "remote", "test"); + config.setString("branch", "branchtopush", "merge", + "refs/heads/upstreambranch"); + config.save(); + + assertThrows(InvalidRefNameException.class, + () -> git.push().setRemote("test") + .setPushDefault(PushDefault.SIMPLE).call()); + } + } + + /** + * Check that if no PushDefault is set, the value is read from the git + * config. + * + * @throws Exception + */ + @Test + public void testPushDefaultFromConfig() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.setString("push", null, "default", "upstream"); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + RevCommit commit = git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + config.setString("branch", "branchtopush", "remote", "test"); + config.setString("branch", "branchtopush", "merge", + "refs/heads/upstreambranch"); + config.save(); + + assertEquals(null, + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/upstreambranch")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + PushCommand cmd = git.push(); + cmd.setRemote("test").setPushDefault(null).call(); + assertEquals(PushDefault.UPSTREAM, cmd.getPushDefault()); + assertEquals(null, + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(commit.getId(), + git2.getRepository().resolve("refs/heads/upstreambranch")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + assertEquals(commit.getId(), git.getRepository() + .resolve("refs/remotes/origin/upstreambranch")); + assertEquals(null, git.getRepository() + .resolve("refs/remotes/origin/branchtopush")); + } + } + + /** + * Check that if no PushDefault is set and none is set in the git config, it + * defaults to "simple". + * + * @throws Exception + */ + @Test + public void testPushDefaultFromConfigDefault() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + RevCommit commit = git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + config.setString("branch", "branchtopush", "remote", "test"); + config.setString("branch", "branchtopush", "merge", + "refs/heads/branchtopush"); + config.save(); + + assertEquals(null, + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + PushCommand cmd = git.push(); + cmd.setRemote("test").setPushDefault(null).call(); + assertEquals(PushDefault.SIMPLE, cmd.getPushDefault()); + assertEquals(commit.getId(), + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + assertEquals(commit.getId(), git.getRepository() + .resolve("refs/remotes/origin/branchtopush")); + } + } + + /** + * Check that branch.<name>.pushRemote overrides anything else. + * + * @throws Exception + */ + @Test + public void testBranchPushRemote() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.setString("remote", null, "pushDefault", "test"); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + config.setString("branch", "branchtopush", "remote", "test"); + config.setString("branch", "branchtopush", "pushremote", "origin"); + config.setString("branch", "branchtopush", "merge", + "refs/heads/branchtopush"); + config.save(); + + assertThrows(InvalidRefNameException.class, () -> git.push() + .setPushDefault(PushDefault.UPSTREAM).call()); + } + } + + /** + * Check that remote.pushDefault overrides branch.<name>.remote + * + * @throws Exception + */ + @Test + public void testRemotePushDefault() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.setString("remote", null, "pushDefault", "origin"); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + config.setString("branch", "branchtopush", "remote", "test"); + config.setString("branch", "branchtopush", "merge", + "refs/heads/branchtopush"); + config.save(); + + assertThrows(InvalidRefNameException.class, () -> git.push() + .setPushDefault(PushDefault.UPSTREAM).call()); + } + } + + /** + * Check that ultimately we fall back to "origin". + * + * @throws Exception + */ + @Test + public void testDefaultRemote() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + config.setString("branch", "branchtopush", "merge", + "refs/heads/branchtopush"); + config.save(); + + PushCommand cmd = git.push().setPushDefault(PushDefault.UPSTREAM); + TransportException e = assertThrows(TransportException.class, + () -> cmd.call()); + assertEquals(NoRemoteRepositoryException.class, + e.getCause().getClass()); + assertEquals("origin", cmd.getRemote()); + } + } + + /** + * Check that a push without specifying a remote or mode or anything can + * succeed if the git config is correct. + * + * @throws Exception + */ + @Test + public void testDefaultPush() throws Exception { + try (Git git = new Git(db); + Git git2 = new Git(createBareRepository())) { + StoredConfig config = git.getRepository().getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish( + git2.getRepository().getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/origin/*")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + RevCommit commit = git.commit().setMessage("adding f").call(); + + git.checkout().setName("not-pushed").setCreateBranch(true).call(); + git.checkout().setName("branchtopush").setCreateBranch(true).call(); + + config = git.getRepository().getConfig(); + config.setString("branch", "branchtopush", "remote", "test"); + config.save(); + + assertEquals(null, + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + // Should use remote "test", push.default=current + PushCommand cmd = git.push(); + cmd.call(); + assertEquals("test", cmd.getRemote()); + assertEquals(PushDefault.CURRENT, cmd.getPushDefault()); + assertEquals(commit.getId(), + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/master")); + assertEquals(commit.getId(), git.getRepository() + .resolve("refs/remotes/origin/branchtopush")); + } + } + + /** * Check that missing refs don't cause errors during push * * @throws Exception @@ -297,6 +1068,7 @@ public class PushCommandTest extends RepositoryTestCase { public void testPushAfterGC() throws Exception { // create other repository Repository db2 = createWorkRepository(); + addRepoToClose(db2); // setup the first repository final StoredConfig config = db.getConfig(); @@ -360,6 +1132,7 @@ public class PushCommandTest extends RepositoryTestCase { // create other repository Repository db2 = createWorkRepository(); + addRepoToClose(db2); // setup the first repository final StoredConfig config = db.getConfig(); 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 86239023dc..c64ff0b1c3 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 @@ -2927,8 +2927,8 @@ public class RebaseCommandTest extends RepositoryTestCase { } } - @Test - public void testRebaseInteractiveFixupWithBlankLines() throws Exception { + private void simpleFixup(String firstMessage, String secondMessage) + throws Exception { // create file1 on master writeTrashFile(FILE1, FILE1); git.add().addFilepattern(FILE1).call(); @@ -2938,13 +2938,13 @@ public class RebaseCommandTest extends RepositoryTestCase { // create file2 on master writeTrashFile("file2", "file2"); git.add().addFilepattern("file2").call(); - git.commit().setMessage("Add file2").call(); + git.commit().setMessage(firstMessage).call(); assertTrue(new File(db.getWorkTree(), "file2").exists()); // update FILE1 on master writeTrashFile(FILE1, "blah"); git.add().addFilepattern(FILE1).call(); - git.commit().setMessage("updated file1 on master\n\nsome text").call(); + git.commit().setMessage(secondMessage).call(); git.rebase().setUpstream("HEAD~2") .runInteractively(new InteractiveHandler() { @@ -2968,9 +2968,31 @@ public class RebaseCommandTest extends RepositoryTestCase { try (RevWalk walk = new RevWalk(db)) { ObjectId headId = db.resolve(Constants.HEAD); RevCommit headCommit = walk.parseCommit(headId); - assertEquals("Add file2", - headCommit.getFullMessage()); + assertEquals(firstMessage, headCommit.getFullMessage()); } + + } + + @Test + public void testRebaseInteractiveFixupWithBlankLines() throws Exception { + simpleFixup("Add file2", "updated file1 on master\n\nsome text"); + } + + @Test + public void testRebaseInteractiveFixupWithBlankLines2() throws Exception { + simpleFixup("Add file2\n\nBody\n", + "updated file1 on master\n\nsome text"); + } + + @Test + public void testRebaseInteractiveFixupWithHash() throws Exception { + simpleFixup("#Add file2", "updated file1 on master"); + } + + @Test + public void testRebaseInteractiveFixupWithHash2() throws Exception { + simpleFixup("#Add file2\n\nHeader has hash\n", + "#updated file1 on master"); } @Test(expected = InvalidRebaseStepException.class) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java index cfa8486ac5..1c7b8d13a8 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java @@ -9,6 +9,7 @@ */ package org.eclipse.jgit.api; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -166,7 +167,9 @@ public class RevertCommandTest extends RepositoryTestCase { checkFile(new File(db.getWorkTree(), "a"), "first\n" + "<<<<<<< master\n" + "second\n" + "third\n" + "=======\n" - + ">>>>>>> " + secondCommit.getId().abbreviate(7).name() + + ">>>>>>> " + + secondCommit.getId() + .abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH).name() + " add second\n"); Iterator<RevCommit> history = git.log().call().iterator(); RevCommit revertCommit = history.next(); @@ -232,7 +235,7 @@ public class RevertCommandTest extends RepositoryTestCase { assertTrue(new File(db.getDirectory(), Constants.MERGE_MSG).exists()); assertEquals("Revert \"" + sideCommit.getShortMessage() + "\"\n\nThis reverts commit " + sideCommit.getId().getName() - + ".\n\nConflicts:\n\ta\n", + + ".\n\n# Conflicts:\n#\ta\n", db.readMergeCommitMsg()); assertTrue(new File(db.getDirectory(), Constants.REVERT_HEAD) .exists()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java index 5311edb0eb..19281f6c99 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java @@ -21,8 +21,10 @@ import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.NoFilepatternException; import org.eclipse.jgit.errors.NoWorkTreeException; import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Sets; import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.util.FS; import org.junit.Test; @@ -181,4 +183,31 @@ public class StatusCommandTest extends RepositoryTestCase { } } + @Test + public void testNestedCommittedGitRepoAndPathFilter() throws Exception { + commitFile("file.txt", "file", "master"); + try (Repository inner = new FileRepositoryBuilder() + .setWorkTree(new File(db.getWorkTree(), "subgit")).build()) { + inner.create(); + writeTrashFile("subgit/sub.txt", "sub"); + try (Git outerGit = new Git(db); Git innerGit = new Git(inner)) { + innerGit.add().addFilepattern("sub.txt").call(); + innerGit.commit().setMessage("Inner commit").call(); + outerGit.add().addFilepattern("subgit").call(); + outerGit.commit().setMessage("Outer commit").call(); + assertTrue(innerGit.status().call().isClean()); + assertTrue(outerGit.status().call().isClean()); + writeTrashFile("subgit/sub.txt", "sub2"); + assertFalse(innerGit.status().call().isClean()); + assertFalse(outerGit.status().call().isClean()); + assertTrue( + outerGit.status().addPath("file.txt").call().isClean()); + assertTrue(outerGit.status().addPath("doesntexist").call() + .isClean()); + assertFalse( + outerGit.status().addPath("subgit").call().isClean()); + } + } + } + } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/BareSuperprojectWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/BareSuperprojectWriterTest.java new file mode 100644 index 0000000000..c3b93879b2 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/BareSuperprojectWriterTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 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 + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.gitrepo; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jgit.gitrepo.BareSuperprojectWriter.BareWriterConfig; +import org.eclipse.jgit.gitrepo.RepoCommand.RemoteReader; +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.junit.Test; + +public class BareSuperprojectWriterTest extends RepositoryTestCase { + + private static final String SHA1_A = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + + @Override + public void setUp() throws Exception { + super.setUp(); + } + + @Test + public void write_setGitModulesContents() throws Exception { + try (Repository bareRepo = createBareRepository()) { + RepoProject repoProject = new RepoProject("subprojectX", "path/to", + "refs/heads/branch-x", "remote", ""); + repoProject.setUrl("http://example.com/a"); + + RemoteReader mockRemoteReader = mock(RemoteReader.class); + when(mockRemoteReader.sha1("http://example.com/a", + "refs/heads/branch-x")) + .thenReturn(ObjectId.fromString(SHA1_A)); + + BareSuperprojectWriter w = new BareSuperprojectWriter(bareRepo, + null, "refs/heads/master", author, mockRemoteReader, + BareWriterConfig.getDefault(), List.of()); + + RevCommit commit = w.write(Arrays.asList(repoProject)); + + String contents = readContents(bareRepo, commit, ".gitmodules"); + List<String> contentLines = Arrays + .asList(contents.split("\n")); + assertThat(contentLines.get(0), + is("[submodule \"subprojectX\"]")); + assertThat(contentLines.subList(1, contentLines.size()), + containsInAnyOrder(is("\tbranch = refs/heads/branch-x"), + is("\tpath = path/to"), + is("\turl = http://example.com/a"))); + } + } + + @Test + public void write_setExtraContents() throws Exception { + try (Repository bareRepo = createBareRepository()) { + RepoProject repoProject = new RepoProject("subprojectX", "path/to", + "refs/heads/branch-x", "remote", ""); + repoProject.setUrl("http://example.com/a"); + + RemoteReader mockRemoteReader = mock(RemoteReader.class); + when(mockRemoteReader.sha1("http://example.com/a", + "refs/heads/branch-x")) + .thenReturn(ObjectId.fromString(SHA1_A)); + + BareSuperprojectWriter w = new BareSuperprojectWriter(bareRepo, + null, "refs/heads/master", author, mockRemoteReader, + BareWriterConfig.getDefault(), + List.of(new BareSuperprojectWriter.ExtraContent("x", + "extra-content"))); + + RevCommit commit = w.write(Arrays.asList(repoProject)); + + String contents = readContents(bareRepo, commit, "x"); + assertThat(contents, is("extra-content")); + } + } + + private String readContents(Repository repo, RevCommit commit, + String path) throws Exception { + String idStr = commit.getId().name() + ":" + path; + ObjectId modId = repo.resolve(idStr); + try (ObjectReader reader = repo.newObjectReader()) { + return new String( + reader.open(modId).getCachedBytes(Integer.MAX_VALUE), + StandardCharsets.UTF_8); + + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java index 20958a812c..c9a0b0b6e5 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java @@ -11,12 +11,16 @@ package org.eclipse.jgit.gitrepo; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; +import java.io.File; import java.io.IOException; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; @@ -152,4 +156,33 @@ public class ManifestParserTest { testNormalize("", ""); testNormalize("a/b", "a/b"); } + + @Test + public void testXXE() throws Exception { + File externalEntity = File.createTempFile("injected", "xml"); + externalEntity.deleteOnExit(); + Files.write(externalEntity.toPath(), + "<evil>injected xml</evil>" + .getBytes(UTF_8), + StandardOpenOption.WRITE); + String baseUrl = "https://git.google.com/"; + StringBuilder xmlContent = new StringBuilder(); + xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") + .append("<!DOCTYPE booo [ <!ENTITY foobar SYSTEM \"") + .append(externalEntity.getPath()).append("\"> ]>\n") + .append("<manifest>") + .append("<remote name=\"remote1\" fetch=\".\" />") + .append("<default revision=\"master\" remote=\"remote1\" />") + .append("&foobar;") + .append("<project path=\"foo\" name=\"foo\" groups=\"a,test\" />") + .append("</manifest>"); + + IOException e = assertThrows(IOException.class, + () -> new ManifestParser(null, null, "master", baseUrl, null, + null) + .read(new ByteArrayInputStream( + xmlContent.toString().getBytes(UTF_8)))); + assertTrue(e.getCause().getMessage().contains("DOCTYPE")); + } + } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java index 509adc2cb2..3e6d13a67e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java @@ -15,6 +15,7 @@ import static org.junit.Assert.assertFalse; 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.BufferedReader; import java.io.ByteArrayInputStream; @@ -546,24 +547,29 @@ public class RepoCommandTest extends RepositoryTestCase { // The original file should exist File hello = new File(localDb.getWorkTree(), "foo/hello.txt"); assertTrue("The original file should exist", hello.exists()); - assertFalse("The original file should not be executable", - hello.canExecute()); + if (FS.DETECTED.supportsExecute()) { + assertFalse("The original file should not be executable", + FS.DETECTED.canExecute(hello)); + } assertContents(hello.toPath(), "master world"); // The dest file should also exist hello = new File(localDb.getWorkTree(), "Hello"); assertTrue("The destination file should exist", hello.exists()); - assertFalse("The destination file should not be executable", - hello.canExecute()); + if (FS.DETECTED.supportsExecute()) { + assertFalse("The destination file should not be executable", + FS.DETECTED.canExecute(hello)); + } assertContents(hello.toPath(), "master world"); } @Test public void testRepoManifestCopyFile_executable() throws Exception { + assumeTrue(FS.DETECTED.supportsExecute()); try (Git git = new Git(defaultDb)) { git.checkout().setName("master").call(); File f = JGitTestUtil.writeTrashFile(defaultDb, "hello.sh", "content of the executable file"); - f.setExecutable(true); + FS.DETECTED.setExecute(f, true); git.add().addFilepattern("hello.sh").call(); git.commit().setMessage("Add binary file").call(); } @@ -588,7 +594,8 @@ public class RepoCommandTest extends RepositoryTestCase { // The original file should exist and be an executable File hello = new File(localDb.getWorkTree(), "foo/hello.sh"); assertTrue("The original file should exist", hello.exists()); - assertTrue("The original file must be executable", hello.canExecute()); + assertTrue("The original file must be executable", + FS.DETECTED.canExecute(hello)); try (BufferedReader reader = Files.newBufferedReader(hello.toPath(), UTF_8)) { String content = reader.readLine(); @@ -600,7 +607,7 @@ public class RepoCommandTest extends RepositoryTestCase { hello = new File(localDb.getWorkTree(), "copy-hello.sh"); assertTrue("The destination file should exist", hello.exists()); assertTrue("The destination file must be executable", - hello.canExecute()); + FS.DETECTED.canExecute(hello)); try (BufferedReader reader = Files.newBufferedReader(hello.toPath(), UTF_8)) { String content = reader.readLine(); 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 new file mode 100644 index 0000000000..c9ebec7638 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalDiffToolTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2020-2021, 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_DIFFTOOL_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_TRUST_EXIT_CODE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jgit.lib.internal.BooleanTriState; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.junit.Test; + +/** + * Testing external diff tools. + */ +public class ExternalDiffToolTest extends ExternalToolTestCase { + + @Test + public void testToolNames() { + 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); + } + + @Test + public void testAllTools() { + DiffTools manager = new DiffTools(db); + Set<String> actualToolNames = manager.getAvailableTools().keySet(); + Set<String> expectedToolNames = new LinkedHashSet<>(); + CommandLineDiffTool[] defaultTools = CommandLineDiffTool.values(); + for (CommandLineDiffTool defaultTool : defaultTools) { + String toolName = defaultTool.name(); + expectedToolNames.add(toolName); + } + assertEquals("Incorrect set of external diff tools", expectedToolNames, + actualToolNames); + } + + @Test + public void testOverridePredefinedToolPath() { + String toolName = CommandLineDiffTool.guiffy.name(); + String customToolPath = "/usr/bin/echo"; + + FileBasedConfig config = db.getConfig(); + config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_CMD, + "echo"); + config.setString(CONFIG_DIFFTOOL_SECTION, toolName, CONFIG_KEY_PATH, + customToolPath); + + DiffTools manager = new DiffTools(db); + Map<String, ExternalDiffTool> tools = manager.getUserDefinedTools(); + ExternalDiffTool diffTool = tools.get(toolName); + assertNotNull("Expected tool \"" + toolName + "\" to be user defined", + diffTool); + + String toolPath = diffTool.getPath(); + assertEquals("Expected external diff tool to have an overriden path", + customToolPath, toolPath); + } + + @Test + public void testUserDefinedTools() { + FileBasedConfig config = db.getConfig(); + String customToolname = "customTool"; + config.setString(CONFIG_DIFFTOOL_SECTION, customToolname, + CONFIG_KEY_CMD, "echo"); + config.setString(CONFIG_DIFFTOOL_SECTION, customToolname, + CONFIG_KEY_PATH, "/usr/bin/echo"); + config.setString(CONFIG_DIFFTOOL_SECTION, customToolname, + CONFIG_KEY_PROMPT, "--no-prompt"); + config.setString(CONFIG_DIFFTOOL_SECTION, customToolname, + CONFIG_KEY_GUITOOL, "--no-gui"); + config.setString(CONFIG_DIFFTOOL_SECTION, customToolname, + CONFIG_KEY_TRUST_EXIT_CODE, "--no-trust-exit-code"); + DiffTools manager = new DiffTools(db); + Set<String> actualToolNames = manager.getUserDefinedTools().keySet(); + Set<String> expectedToolNames = new LinkedHashSet<>(); + expectedToolNames.add(customToolname); + assertEquals("Incorrect set of external diff tools", expectedToolNames, + actualToolNames); + } + + @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); + } + + @Test + public void testCompare() { + DiffTools manager = new DiffTools(db); + + String newPath = ""; + String oldPath = ""; + String newId = ""; + String oldId = ""; + String toolName = ""; + BooleanTriState prompt = BooleanTriState.UNSET; + BooleanTriState gui = BooleanTriState.UNSET; + BooleanTriState trustExitCode = BooleanTriState.UNSET; + + 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); + } + + @Test + public void testDefaultTool() throws Exception { + FileBasedConfig config = db.getConfig(); + // the default diff tool is configured without a subsection + String subsection = null; + config.setString("diff", subsection, "tool", "customTool"); + + DiffTools manager = new DiffTools(db); + BooleanTriState gui = BooleanTriState.UNSET; + String defaultToolName = manager.getDefaultToolName(gui); + assertEquals( + "Expected configured difftool to be the default external diff tool", + "my_default_toolname", defaultToolName); + + gui = BooleanTriState.TRUE; + String defaultGuiToolName = manager.getDefaultToolName(gui); + assertEquals( + "Expected configured difftool to be the default external diff tool", + "my_gui_tool", defaultGuiToolName); + + config.setString("diff", subsection, "guitool", "customGuiTool"); + manager = new DiffTools(db); + defaultGuiToolName = manager.getDefaultToolName(gui); + assertEquals( + "Expected configured difftool to be the default external diff guitool", + "my_gui_tool", defaultGuiToolName); + } +} 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 new file mode 100644 index 0000000000..0cc12978a8 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/diffmergetool/ExternalToolTestCase.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020-2021, 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 java.io.File; +import java.nio.file.Files; + +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.FS_POSIX; +import org.junit.After; +import org.junit.Assume; +import org.junit.Before; + +/** + * Base test case for external merge and diff tool tests. + */ +public abstract class ExternalToolTestCase extends RepositoryTestCase { + + protected static final String DEFAULT_CONTENT = "line1"; + + protected File localFile; + + protected File remoteFile; + + protected File mergedFile; + + protected File baseFile; + + protected File commandResult; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + + localFile = writeTrashFile("localFile.txt", DEFAULT_CONTENT + "\n"); + localFile.deleteOnExit(); + remoteFile = writeTrashFile("remoteFile.txt", DEFAULT_CONTENT + "\n"); + remoteFile.deleteOnExit(); + mergedFile = writeTrashFile("mergedFile.txt", ""); + mergedFile.deleteOnExit(); + baseFile = writeTrashFile("baseFile.txt", ""); + baseFile.deleteOnExit(); + commandResult = writeTrashFile("commandResult.txt", ""); + commandResult.deleteOnExit(); + } + + @After + @Override + public void tearDown() throws Exception { + Files.delete(localFile.toPath()); + Files.delete(remoteFile.toPath()); + Files.delete(mergedFile.toPath()); + Files.delete(baseFile.toPath()); + Files.delete(commandResult.toPath()); + + super.tearDown(); + } + + + protected static void assumePosixPlatform() { + Assume.assumeTrue( + "This test can run only in Linux tests", + FS.DETECTED instanceof FS_POSIX); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java index 4f1314057f..ab588cb71e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java @@ -15,16 +15,19 @@ import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.LongStream; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.IndexEventConsumer; import org.eclipse.jgit.internal.storage.pack.PackExt; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.junit.TestRng; @@ -154,6 +157,119 @@ public class DfsBlockCacheTest { @SuppressWarnings("resource") @Test + public void hasIndexEventConsumerOnlyLoaded() throws Exception { + AtomicInteger loaded = new AtomicInteger(); + IndexEventConsumer indexEventConsumer = new IndexEventConsumer() { + @Override + public void acceptRequestedEvent(int packExtPos, boolean cacheHit, + long loadMicros, long bytes, + Duration lastEvictionDuration) { + assertEquals(PackExt.INDEX.getPosition(), packExtPos); + assertTrue(cacheHit); + assertTrue(lastEvictionDuration.isZero()); + loaded.incrementAndGet(); + } + }; + + DfsBlockCache.reconfigure(new DfsBlockCacheConfig().setBlockSize(512) + .setBlockLimit(512 * 4) + .setIndexEventConsumer(indexEventConsumer)); + cache = DfsBlockCache.getInstance(); + + DfsRepositoryDescription repo = new DfsRepositoryDescription("test"); + InMemoryRepository r1 = new InMemoryRepository(repo); + byte[] content = rng.nextBytes(424242); + ObjectId id; + try (ObjectInserter ins = r1.newObjectInserter()) { + id = ins.insert(OBJ_BLOB, content); + ins.flush(); + } + + try (ObjectReader rdr = r1.newObjectReader()) { + byte[] actual = rdr.open(id, OBJ_BLOB).getBytes(); + assertTrue(Arrays.equals(content, actual)); + } + // All cache entries are hot and cache is at capacity. + assertTrue(LongStream.of(cache.getHitCount()).sum() > 0); + assertEquals(99, cache.getFillPercentage()); + + InMemoryRepository r2 = new InMemoryRepository(repo); + content = rng.nextBytes(424242); + try (ObjectInserter ins = r2.newObjectInserter()) { + ins.insert(OBJ_BLOB, content); + ins.flush(); + } + assertTrue(cache.getEvictions()[PackExt.PACK.getPosition()] > 0); + assertEquals(1, cache.getEvictions()[PackExt.INDEX.getPosition()]); + assertEquals(1, loaded.get()); + } + + @SuppressWarnings("resource") + @Test + public void hasIndexEventConsumerLoadedAndEvicted() throws Exception { + AtomicInteger loaded = new AtomicInteger(); + AtomicInteger evicted = new AtomicInteger(); + IndexEventConsumer indexEventConsumer = new IndexEventConsumer() { + @Override + public void acceptRequestedEvent(int packExtPos, boolean cacheHit, + long loadMicros, long bytes, + Duration lastEvictionDuration) { + assertEquals(PackExt.INDEX.getPosition(), packExtPos); + assertTrue(cacheHit); + assertTrue(lastEvictionDuration.isZero()); + loaded.incrementAndGet(); + } + + @Override + public void acceptEvictedEvent(int packExtPos, long bytes, + int totalCacheHitCount, Duration lastEvictionDuration) { + assertEquals(PackExt.INDEX.getPosition(), packExtPos); + assertTrue(totalCacheHitCount > 0); + assertTrue(lastEvictionDuration.isZero()); + evicted.incrementAndGet(); + } + + @Override + public boolean shouldReportEvictedEvent() { + return true; + } + }; + + DfsBlockCache.reconfigure(new DfsBlockCacheConfig().setBlockSize(512) + .setBlockLimit(512 * 4) + .setIndexEventConsumer(indexEventConsumer)); + cache = DfsBlockCache.getInstance(); + + DfsRepositoryDescription repo = new DfsRepositoryDescription("test"); + InMemoryRepository r1 = new InMemoryRepository(repo); + byte[] content = rng.nextBytes(424242); + ObjectId id; + try (ObjectInserter ins = r1.newObjectInserter()) { + id = ins.insert(OBJ_BLOB, content); + ins.flush(); + } + + try (ObjectReader rdr = r1.newObjectReader()) { + byte[] actual = rdr.open(id, OBJ_BLOB).getBytes(); + assertTrue(Arrays.equals(content, actual)); + } + // All cache entries are hot and cache is at capacity. + assertTrue(LongStream.of(cache.getHitCount()).sum() > 0); + assertEquals(99, cache.getFillPercentage()); + + InMemoryRepository r2 = new InMemoryRepository(repo); + content = rng.nextBytes(424242); + try (ObjectInserter ins = r2.newObjectInserter()) { + ins.insert(OBJ_BLOB, content); + ins.flush(); + } + assertTrue(cache.getEvictions()[PackExt.PACK.getPosition()] > 0); + assertEquals(1, cache.getEvictions()[PackExt.INDEX.getPosition()]); + assertEquals(1, loaded.get()); + assertEquals(1, evicted.get()); + } + + @Test public void noConcurrencySerializedReads_oneRepo() throws Exception { InMemoryRepository r1 = createRepoWithBitmap("test"); // Reset cache with concurrency Level at 1 i.e. no concurrency. @@ -267,7 +383,6 @@ public class DfsBlockCacheTest { assertEquals(2, cache.getMissCount()[0]); } - @SuppressWarnings("resource") @Test public void highConcurrencyParallelReads_oneRepo() throws Exception { InMemoryRepository r1 = createRepoWithBitmap("test"); @@ -290,6 +405,30 @@ public class DfsBlockCacheTest { assertEquals(1, cache.getMissCount()[0]); } + @Test + public void highConcurrencyParallelReads_oneRepoParallelReverseIndex() + throws Exception { + InMemoryRepository r1 = createRepoWithBitmap("test"); + resetCache(); + + DfsReader reader = (DfsReader) r1.newObjectReader(); + reader.getOptions().setLoadRevIndexInParallel(true); + for (DfsPackFile pack : r1.getObjectDatabase().getPacks()) { + // Only load non-garbage pack with bitmap. + if (pack.isGarbage()) { + continue; + } + asyncRun(() -> pack.getBitmapIndex(reader)); + asyncRun(() -> pack.getPackIndex(reader)); + asyncRun(() -> pack.getBitmapIndex(reader)); + } + waitForExecutorPoolTermination(); + + assertEquals(1, cache.getMissCount()[PackExt.BITMAP_INDEX.ordinal()]); + assertEquals(1, cache.getMissCount()[PackExt.INDEX.ordinal()]); + assertEquals(1, cache.getMissCount()[0]); + } + private void resetCache() { resetCache(32); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java index 6357a0b9a8..daf4382719 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java @@ -42,6 +42,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Predicate; +import java.util.function.Function; +import java.util.stream.Collectors; import org.eclipse.jgit.events.ListenerHandle; import org.eclipse.jgit.events.RefsChangedListener; @@ -127,6 +129,7 @@ public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase { } diskRepo = fileRepo; + addRepoToClose(diskRepo); setLogAllRefUpdates(true); if (!useReftable) { @@ -1190,7 +1193,8 @@ public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase { } Map<String, Ref> refs = diskRepo.getRefDatabase() - .getRefs(RefDatabase.ALL); + .getRefsByPrefix(RefDatabase.ALL).stream() + .collect(Collectors.toMap(Ref::getName, Function.identity())); Ref actualHead = refs.remove(Constants.HEAD); if (actualHead != null) { String actualLeafName = actualHead.getLeaf().getName(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableStackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableStackTest.java index 6c74f0079a..6c7992716c 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableStackTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableStackTest.java @@ -14,6 +14,7 @@ import static org.eclipse.jgit.lib.Ref.Storage.PACKED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; import java.io.File; import java.io.FileNotFoundException; @@ -32,10 +33,12 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.util.FileUtils; +import org.eclipse.jgit.util.SystemReader; import org.junit.After; import org.junit.Before; import org.junit.Test; + public class FileReftableStackTest { private static Ref newRef(String name, ObjectId id) { @@ -115,9 +118,12 @@ public class FileReftableStackTest { testCompaction(1024); } - @SuppressWarnings({ "resource", "unused" }) + @SuppressWarnings("resource") @Test public void missingReftable() throws Exception { + // Can't delete in-use files on Windows. + assumeFalse(SystemReader.getInstance().isWindows()); + try (FileReftableStack stack = new FileReftableStack( new File(reftableDir, "refs"), reftableDir, null, () -> new Config())) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderAfterOpenConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderAfterOpenConfigTest.java new file mode 100644 index 0000000000..100bd32ad8 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderAfterOpenConfigTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.internal.storage.file; + +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.CoreConfig; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.util.SystemReader; +import org.junit.Before; + +public class FileRepositoryBuilderAfterOpenConfigTest extends FileRepositoryBuilderTest { + /** {@inheritDoc} */ + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + StoredConfig userConfig = SystemReader.getInstance().getUserConfig(); + userConfig.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_TRUST_PACKED_REFS_STAT, + CoreConfig.TrustPackedRefsStat.AFTER_OPEN); + userConfig.save(); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java index bfb233f77f..48f6e06385 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java @@ -42,6 +42,7 @@ public abstract class GcTestCase extends LocalDiskRepositoryTestCase { @Override @After public void tearDown() throws Exception { + tr.close(); super.tearDown(); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java index 1ff2264f67..3fe8f52fba 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java @@ -722,6 +722,7 @@ public class PackWriterTest extends SampleDataRepositoryTestCase { */ private FileRepository setUpRepoWithMultiplePackfiles() throws Exception { FileRepository fileRepository = createWorkRepository(); + addRepoToClose(fileRepository); try (Git git = new Git(fileRepository)) { // Creates 2 objects (C1 = commit, T1 = tree) git.commit().setMessage("First commit").call(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryAfterOpenConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryAfterOpenConfigTest.java new file mode 100644 index 0000000000..42304e2253 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryAfterOpenConfigTest.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.internal.storage.file; + +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.CoreConfig; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.util.SystemReader; + +public class RefDirectoryAfterOpenConfigTest extends RefDirectoryTest { + /** {@inheritDoc} */ + @Override + public void refDirectorySetup() throws Exception { + StoredConfig userConfig = SystemReader.getInstance().getUserConfig(); + userConfig.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_TRUST_PACKED_REFS_STAT, + CoreConfig.TrustPackedRefsStat.AFTER_OPEN); + userConfig.save(); + super.refDirectorySetup(); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java index 38c545ef57..4b80f18970 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java @@ -72,7 +72,10 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase { @Before public void setUp() throws Exception { super.setUp(); + refDirectorySetup(); + } + public void refDirectorySetup() throws Exception { diskRepo = createBareRepository(); refdir = (RefDirectory) diskRepo.getRefDatabase(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/io/CancellableDigestOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/io/CancellableDigestOutputStreamTest.java new file mode 100644 index 0000000000..09a7c0b28a --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/io/CancellableDigestOutputStreamTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022, Tencent. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.internal.storage.io; + +import static org.eclipse.jgit.internal.storage.io.CancellableDigestOutputStream.BYTES_TO_WRITE_BEFORE_CANCEL_CHECK; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import java.io.InterruptedIOException; + +import org.eclipse.jgit.lib.ProgressMonitor; +import org.eclipse.jgit.util.io.NullOutputStream; +import org.junit.Test; + +public class CancellableDigestOutputStreamTest { + private static class CancelledTestMonitor implements ProgressMonitor { + + private boolean cancelled = false; + + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @Override + public void start(int totalTasks) { + // not implemented + } + + @Override + public void beginTask(String title, int totalWork) { + // not implemented + } + + @Override + public void update(int completed) { + // not implemented + } + + @Override + public void endTask() { + // not implemented + } + + @Override + public boolean isCancelled() { + return cancelled; + } + } + + @Test + public void testCancelInProcess() throws Exception { + CancelledTestMonitor m = new CancelledTestMonitor(); + try (CancellableDigestOutputStream out = new CancellableDigestOutputStream( + m, NullOutputStream.INSTANCE)) { + byte[] KB = new byte[1024]; + int triggerCancelWriteCnt = BYTES_TO_WRITE_BEFORE_CANCEL_CHECK + / KB.length; + for (int i = 0; i < triggerCancelWriteCnt + 1; i++) { + out.write(KB); + } + assertTrue(out.length() > BYTES_TO_WRITE_BEFORE_CANCEL_CHECK); + m.setCancelled(true); + + for (int i = 0; i < triggerCancelWriteCnt - 1; i++) { + out.write(KB); + } + + long lastLength = out.length(); + assertThrows(InterruptedIOException.class, () -> { + out.write(1); + }); + assertEquals(lastLength, out.length()); + + assertThrows(InterruptedIOException.class, () -> { + out.write(new byte[1]); + }); + assertEquals(lastLength, out.length()); + } + } + + @Test + public void testTriggerCheckAfterSingleBytes() throws Exception { + CancelledTestMonitor m = new CancelledTestMonitor(); + try (CancellableDigestOutputStream out = new CancellableDigestOutputStream( + m, NullOutputStream.INSTANCE)) { + + byte[] bytes = new byte[BYTES_TO_WRITE_BEFORE_CANCEL_CHECK + 1]; + m.setCancelled(true); + + assertThrows(InterruptedIOException.class, () -> { + out.write(bytes); + }); + assertEquals(BYTES_TO_WRITE_BEFORE_CANCEL_CHECK, out.length()); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFileTest.java index 11741b41aa..d065280778 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFileTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFileTest.java @@ -369,20 +369,21 @@ public class OpenSshConfigFileTest extends RepositoryTestCase { @Test public void testListValueSingle() throws Exception { - config("Host orcz\nUserKnownHostsFile /foo/bar\n"); + config("Host orcz\nUserKnownHostsFile ~/foo/bar\n"); final HostConfig c = lookup("orcz"); assertNotNull(c); - assertEquals("/foo/bar", c.getValue("UserKnownHostsFile")); + assertEquals(new File(home, "foo/bar").getPath(), + c.getValue("UserKnownHostsFile")); } @Test public void testListValueMultiple() throws Exception { // Tilde expansion occurs within the parser - config("Host orcz\nUserKnownHostsFile \"~/foo/ba z\" /foo/bar \n"); + config("Host orcz\nUserKnownHostsFile \"~/foo/ba z\" ~/foo/bar \n"); final HostConfig c = lookup("orcz"); assertNotNull(c); assertArrayEquals(new Object[] { new File(home, "foo/ba z").getPath(), - "/foo/bar" }, + new File(home, "foo/bar").getPath() }, c.getValues("UserKnownHostsFile").toArray()); } @@ -403,22 +404,23 @@ public class OpenSshConfigFileTest extends RepositoryTestCase { @Test public void testIdentityFile() throws Exception { - config("Host orcz\nIdentityFile \"~/foo/ba z\"\nIdentityFile /foo/bar"); + config("Host orcz\nIdentityFile \"~/foo/ba z\"\nIdentityFile ~/foo/bar"); final HostConfig h = lookup("orcz"); assertNotNull(h); // Does tilde replacement assertArrayEquals(new Object[] { new File(home, "foo/ba z").getPath(), - "/foo/bar" }, + new File(home, "foo/bar").getPath() }, h.getValues(SshConstants.IDENTITY_FILE).toArray()); } @Test public void testMultiIdentityFile() throws Exception { - config("IdentityFile \"~/foo/ba z\"\nHost orcz\nIdentityFile /foo/bar\nHOST *\nIdentityFile /foo/baz"); + config("IdentityFile \"~/foo/ba z\"\nHost orcz\nIdentityFile ~/foo/bar\nHOST *\nIdentityFile ~/foo/baz"); final HostConfig h = lookup("orcz"); assertNotNull(h); assertArrayEquals(new Object[] { new File(home, "foo/ba z").getPath(), - "/foo/bar", "/foo/baz" }, + new File(home, "foo/bar").getPath(), + new File(home, "foo/baz").getPath() }, h.getValues(SshConstants.IDENTITY_FILE).toArray()); } @@ -434,23 +436,23 @@ public class OpenSshConfigFileTest extends RepositoryTestCase { @Test public void testPattern() throws Exception { - config("Host repo.or.cz\nIdentityFile ~/foo/bar\nHOST *.or.cz\nIdentityFile /foo/baz"); + config("Host repo.or.cz\nIdentityFile ~/foo/bar\nHOST *.or.cz\nIdentityFile ~/foo/baz"); final HostConfig h = lookup("repo.or.cz"); assertNotNull(h); assertIdentity(new File(home, "foo/bar"), h); assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath(), - "/foo/baz" }, + new File(home, "foo/baz").getPath() }, h.getValues(SshConstants.IDENTITY_FILE).toArray()); } @Test public void testMultiHost() throws Exception { - config("Host orcz *.or.cz\nIdentityFile ~/foo/bar\nHOST *.or.cz\nIdentityFile /foo/baz"); + config("Host orcz *.or.cz\nIdentityFile ~/foo/bar\nHOST *.or.cz\nIdentityFile ~/foo/baz"); final HostConfig h1 = lookup("repo.or.cz"); assertNotNull(h1); assertIdentity(new File(home, "foo/bar"), h1); assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath(), - "/foo/baz" }, + new File(home, "foo/baz").getPath() }, h1.getValues(SshConstants.IDENTITY_FILE).toArray()); final HostConfig h2 = lookup("orcz"); assertNotNull(h2); @@ -547,18 +549,36 @@ public class OpenSshConfigFileTest extends RepositoryTestCase { @Test public void testEnVarSubstitution() throws Exception { - config("Host orcz\nIdentityFile /tmp/${TST_VAR}\n" - + "CertificateFile /tmp/${}/foo\nUser ${TST_VAR}\nIdentityAgent /tmp/${TST_VAR/bar"); + config("Host orcz\nIdentityFile ~/tmp/${TST_VAR}\n" + + "CertificateFile ~/tmp/${}/foo\nUser ${TST_VAR}\nIdentityAgent ~/tmp/${TST_VAR/bar"); HostConfig h = lookup("orcz"); assertNotNull(h); - assertEquals("/tmp/TEST", + File tmp = new File(home, "tmp"); + assertEquals(new File(tmp, "TEST").getPath(), h.getValue(SshConstants.IDENTITY_FILE)); // No variable name - assertEquals("/tmp/${}/foo", h.getValue(SshConstants.CERTIFICATE_FILE)); + assertEquals(new File(new File(tmp, "${}"), "foo").getPath(), + h.getValue(SshConstants.CERTIFICATE_FILE)); // User doesn't get env var substitution: assertUser("${TST_VAR}", h); // Unterminated: - assertEquals("/tmp/${TST_VAR/bar", + assertEquals(new File(new File(tmp, "${TST_VAR"), "bar").getPath(), + h.getValue(SshConstants.IDENTITY_AGENT)); + } + + @Test + public void testIdentityAgentNone() throws Exception { + config("Host orcz\nIdentityAgent none\n"); + HostConfig h = lookup("orcz"); + assertEquals(SshConstants.NONE, + h.getValue(SshConstants.IDENTITY_AGENT)); + } + + @Test + public void testIdentityAgentSshAuthSock() throws Exception { + config("Host orcz\nIdentityAgent SSH_AUTH_SOCK\n"); + HostConfig h = lookup("orcz"); + assertEquals(SshConstants.ENV_SSH_AUTH_SOCKET, h.getValue(SshConstants.IDENTITY_AGENT)); } @@ -607,13 +627,16 @@ public class OpenSshConfigFileTest extends RepositoryTestCase { @Test public void testMultipleMatch() throws Exception { - config("Host foo.bar\nPort 29418\nIdentityFile /foo\n\n" - + "Host *.bar\nPort 22\nIdentityFile /bar\n" - + "Host foo.bar\nPort 47\nIdentityFile /baz\n"); + config("Host foo.bar\nPort 29418\nIdentityFile ~/foo\n\n" + + "Host *.bar\nPort 22\nIdentityFile ~/bar\n" + + "Host foo.bar\nPort 47\nIdentityFile ~/baz\n"); HostConfig h = lookup("foo.bar"); assertNotNull(h); assertPort(29418, h); - assertArrayEquals(new Object[] { "/foo", "/bar", "/baz" }, + assertArrayEquals( + new Object[] { new File(home, "foo").getPath(), + new File(home, "bar").getPath(), + new File(home, "baz").getPath() }, h.getValues(SshConstants.IDENTITY_FILE).toArray()); } @@ -633,4 +656,61 @@ public class OpenSshConfigFileTest extends RepositoryTestCase { assertNotNull(h); assertPort(22, h); } + + @Test + public void testTimeSpec() throws Exception { + assertEquals(-1, OpenSshConfigFile.timeSpec(null)); + assertEquals(-1, OpenSshConfigFile.timeSpec("")); + assertEquals(-1, OpenSshConfigFile.timeSpec(" ")); + assertEquals(-1, OpenSshConfigFile.timeSpec("s")); + assertEquals(-1, OpenSshConfigFile.timeSpec(" s")); + assertEquals(-1, OpenSshConfigFile.timeSpec(" +s")); + assertEquals(-1, OpenSshConfigFile.timeSpec(" -s")); + assertEquals(-1, OpenSshConfigFile.timeSpec("1ms")); + assertEquals(600, OpenSshConfigFile.timeSpec("600")); + assertEquals(600, OpenSshConfigFile.timeSpec("600s")); + assertEquals(600, OpenSshConfigFile.timeSpec(" 600s")); + assertEquals(600, OpenSshConfigFile.timeSpec(" 600s ")); + assertEquals(600, OpenSshConfigFile.timeSpec("\t600s")); + assertEquals(600, OpenSshConfigFile.timeSpec(" \t600 ")); + assertEquals(-1, OpenSshConfigFile.timeSpec(" 600 s ")); + assertEquals(-1, OpenSshConfigFile.timeSpec("600 s")); + assertEquals(600, OpenSshConfigFile.timeSpec("10m")); + assertEquals(5400, OpenSshConfigFile.timeSpec("1h30m")); + assertEquals(5400, OpenSshConfigFile.timeSpec("1h 30m")); + assertEquals(5400, OpenSshConfigFile.timeSpec("1h \t30m")); + assertEquals(5400, OpenSshConfigFile.timeSpec("1h+30m")); + assertEquals(5400, OpenSshConfigFile.timeSpec("1h +30m")); + assertEquals(-1, OpenSshConfigFile.timeSpec("1h + 30m")); + assertEquals(-1, OpenSshConfigFile.timeSpec("1h -30m")); + assertEquals(3630, OpenSshConfigFile.timeSpec("1h30s")); + assertEquals(5400, OpenSshConfigFile.timeSpec("30m 1h")); + assertEquals(3600, OpenSshConfigFile.timeSpec("30m 30m")); + assertEquals(60, OpenSshConfigFile.timeSpec("30 30")); + assertEquals(0, OpenSshConfigFile.timeSpec("0")); + assertEquals(1, OpenSshConfigFile.timeSpec("1")); + assertEquals(1, OpenSshConfigFile.timeSpec("1S")); + assertEquals(1, OpenSshConfigFile.timeSpec("1s")); + assertEquals(60, OpenSshConfigFile.timeSpec("1M")); + assertEquals(60, OpenSshConfigFile.timeSpec("1m")); + assertEquals(3600, OpenSshConfigFile.timeSpec("1H")); + assertEquals(3600, OpenSshConfigFile.timeSpec("1h")); + assertEquals(86400, OpenSshConfigFile.timeSpec("1D")); + assertEquals(86400, OpenSshConfigFile.timeSpec("1d")); + assertEquals(604800, OpenSshConfigFile.timeSpec("1W")); + assertEquals(604800, OpenSshConfigFile.timeSpec("1w")); + assertEquals(172800, OpenSshConfigFile.timeSpec("2d")); + assertEquals(604800, OpenSshConfigFile.timeSpec("1w")); + assertEquals(604800 + 172800 + 3 * 3600 + 30 * 60 + 10, + OpenSshConfigFile.timeSpec("1w2d3h30m10s")); + assertEquals(-1, OpenSshConfigFile.timeSpec("-7")); + assertEquals(-1, OpenSshConfigFile.timeSpec("-9d")); + assertEquals(Integer.MAX_VALUE, OpenSshConfigFile + .timeSpec(Integer.toString(Integer.MAX_VALUE))); + assertEquals(-1, OpenSshConfigFile + .timeSpec(Long.toString(Integer.MAX_VALUE + 1L))); + assertEquals(-1, OpenSshConfigFile + .timeSpec(Integer.toString(Integer.MAX_VALUE / 60 + 1) + 'M')); + assertEquals(-1, OpenSshConfigFile.timeSpec("1000000000000000000000w")); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/AbbrevConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/AbbrevConfigTest.java new file mode 100644 index 0000000000..96ace08dd1 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/AbbrevConfigTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2022, Matthias Sohn <matthias.sohn@sap.com> and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.lib; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.io.IOException; + +import org.eclipse.jgit.api.errors.InvalidConfigurationException; +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.junit.Test; + +public class AbbrevConfigTest extends RepositoryTestCase { + + @Test + public void testDefault() throws Exception { + assertEquals(7, testCoreAbbrev(null)); + } + + @Test + public void testAuto() throws Exception { + assertEquals(7, testCoreAbbrev("auto")); + } + + @Test + public void testNo() throws Exception { + assertEquals(40, testCoreAbbrev("no")); + } + + @Test + public void testValidMin() throws Exception { + assertEquals(4, testCoreAbbrev("4")); + } + + @Test + public void testValid() throws Exception { + assertEquals(22, testCoreAbbrev("22")); + } + + @Test + public void testValidMax() throws Exception { + assertEquals(40, testCoreAbbrev("40")); + } + + @Test + public void testInvalid() { + assertThrows(InvalidConfigurationException.class, + () -> testCoreAbbrev("foo")); + } + + @Test + public void testInvalid2() { + assertThrows(InvalidConfigurationException.class, + () -> testCoreAbbrev("2k")); + } + + @Test + public void testInvalidNegative() { + assertThrows(InvalidConfigurationException.class, + () -> testCoreAbbrev("-1000")); + } + + @Test + public void testInvalidBelowRange() { + assertThrows(InvalidConfigurationException.class, + () -> testCoreAbbrev("3")); + } + + @Test + public void testInvalidBelowRange2() { + assertThrows(InvalidConfigurationException.class, + () -> testCoreAbbrev("-1")); + } + + @Test + public void testInvalidAboveRange() { + assertThrows(InvalidConfigurationException.class, + () -> testCoreAbbrev("41")); + } + + @Test + public void testInvalidAboveRange2() { + assertThrows(InvalidConfigurationException.class, + () -> testCoreAbbrev("100000")); + } + + @Test + public void testToStringNo() + throws InvalidConfigurationException, IOException { + assertEquals("40", setCoreAbbrev("no").toString()); + } + + @Test + public void testToString() + throws InvalidConfigurationException, IOException { + assertEquals("7", setCoreAbbrev("auto").toString()); + } + + @Test + public void testToString12() + throws InvalidConfigurationException, IOException { + assertEquals("12", setCoreAbbrev("12").toString()); + } + + private int testCoreAbbrev(String value) + throws InvalidConfigurationException, IOException { + return setCoreAbbrev(value).get(); + } + + private AbbrevConfig setCoreAbbrev(String value) + throws IOException, InvalidConfigurationException { + FileBasedConfig config = db.getConfig(); + config.setString(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_ABBREV, value); + config.save(); + return AbbrevConfig.parseFromConfig(db); + } + +} 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 new file mode 100644 index 0000000000..d95d7814e4 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitConfigTest.java @@ -0,0 +1,179 @@ +/* + * 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.lib; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.CommitConfig.CleanupMode; +import org.junit.Test; + +public class CommitConfigTest { + + @Test + public void testDefaults() throws Exception { + CommitConfig cfg = parse(""); + assertEquals("Unexpected clean-up mode", CleanupMode.DEFAULT, + cfg.getCleanupMode()); + } + + @Test + public void testCommitCleanup() throws Exception { + String[] values = { "strip", "whitespace", "verbatim", "scissors", + "default" }; + CleanupMode[] expected = { CleanupMode.STRIP, CleanupMode.WHITESPACE, + CleanupMode.VERBATIM, CleanupMode.SCISSORS, + CleanupMode.DEFAULT }; + for (int i = 0; i < values.length; i++) { + CommitConfig cfg = parse("[commit]\n\tcleanup = " + values[i]); + assertEquals("Unexpected clean-up mode", expected[i], + cfg.getCleanupMode()); + } + } + + @Test + public void testResolve() throws Exception { + String[] values = { "strip", "whitespace", "verbatim", "scissors", + "default" }; + CleanupMode[] expected = { CleanupMode.STRIP, CleanupMode.WHITESPACE, + CleanupMode.VERBATIM, CleanupMode.SCISSORS, + CleanupMode.DEFAULT }; + for (int i = 0; i < values.length; i++) { + CommitConfig cfg = parse("[commit]\n\tcleanup = " + values[i]); + for (CleanupMode mode : CleanupMode.values()) { + for (int j = 0; j < 2; j++) { + CleanupMode resolved = cfg.resolve(mode, j == 0); + if (mode != CleanupMode.DEFAULT) { + assertEquals("Clean-up mode should be unchanged", mode, + resolved); + } else if (i + 1 < values.length) { + assertEquals("Unexpected clean-up mode", expected[i], + resolved); + } else { + assertEquals("Unexpected clean-up mode", + j == 0 ? CleanupMode.STRIP + : CleanupMode.WHITESPACE, + resolved); + } + } + } + } + } + + @Test + public void testCleanDefaultThrows() throws Exception { + assertThrows(IllegalArgumentException.class, () -> CommitConfig + .cleanText("Whatever", CleanupMode.DEFAULT, '#')); + } + + @Test + public void testCleanVerbatim() throws Exception { + String message = "\n \nWhatever \n\n\n# A comment\n\nMore\t \n\n\n"; + assertEquals("Unexpected message change", message, + CommitConfig.cleanText(message, CleanupMode.VERBATIM, '#')); + } + + @Test + public void testCleanWhitespace() throws Exception { + String message = "\n \nWhatever \n\n\n# A comment\n\nMore\t \n\n\n"; + assertEquals("Unexpected message change", + "Whatever\n\n# A comment\n\nMore\n", + CommitConfig.cleanText(message, CleanupMode.WHITESPACE, '#')); + } + + @Test + public void testCleanStrip() throws Exception { + String message = "\n \nWhatever \n\n\n# A comment\n\nMore\t \n\n\n"; + assertEquals("Unexpected message change", "Whatever\n\nMore\n", + CommitConfig.cleanText(message, CleanupMode.STRIP, '#')); + } + + @Test + public void testCleanStripCustomChar() throws Exception { + String message = "\n \nWhatever \n\n\n# Not a comment\n\n <A comment\nMore\t \n\n\n"; + assertEquals("Unexpected message change", + "Whatever\n\n# Not a comment\n\nMore\n", + CommitConfig.cleanText(message, CleanupMode.STRIP, '<')); + } + + @Test + public void testCleanScissors() throws Exception { + String message = "\n \nWhatever \n\n\n# Not a comment\n\n <A comment\nMore\t \n\n\n" + + "# ------------------------ >8 ------------------------\n" + + "More\nMore\n"; + assertEquals("Unexpected message change", + "Whatever\n\n# Not a comment\n\n <A comment\nMore\n", + CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#')); + } + + @Test + public void testCleanScissorsCustomChar() throws Exception { + String message = "\n \nWhatever \n\n\n# Not a comment\n\n <A comment\nMore\t \n\n\n" + + "< ------------------------ >8 ------------------------\n" + + "More\nMore\n"; + assertEquals("Unexpected message change", + "Whatever\n\n# Not a comment\n\n <A comment\nMore\n", + CommitConfig.cleanText(message, CleanupMode.SCISSORS, '<')); + } + + @Test + public void testCleanScissorsAtTop() throws Exception { + String message = "# ------------------------ >8 ------------------------\n" + + "\n \nWhatever \n\n\n# Not a comment\n\n <A comment\nMore\t \n\n\n" + + "More\nMore\n"; + assertEquals("Unexpected message change", "", + CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#')); + } + + @Test + public void testCleanScissorsNoScissor() throws Exception { + String message = "\n \nWhatever \n\n\n# A comment\n\nMore\t \n\n\n"; + assertEquals("Unexpected message change", + "Whatever\n\n# A comment\n\nMore\n", + CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#')); + } + + @Test + public void testCleanScissorsNoScissor2() throws Exception { + String message = "Text\n" + + "## ------------------------ >8 ------------------------\n" + + "More\nMore\n"; + assertEquals("Unexpected message change", message, + CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#')); + } + + @Test + public void testCleanScissorsNoScissor3() throws Exception { + String message = "Text\n" + // Wrong number of dashes + + "# ----------------------- >8 ------------------------\n" + + "More\nMore\n"; + assertEquals("Unexpected message change", message, + CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#')); + } + + @Test + public void testCleanScissorsAtEnd() throws Exception { + String message = "Text\n" + + "# ------------------------ >8 ------------------------\n"; + assertEquals("Unexpected message change", "Text\n", + CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#')); + } + + private static CommitConfig parse(String content) + throws ConfigInvalidException { + Config c = new Config(); + c.fromText(content); + return c.get(CommitConfig.KEY); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java index 6dbe30af2d..42bafb60ca 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 SAP SE and others + * Copyright (C) 2021, 2022 SAP SE and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -47,8 +47,7 @@ public class CommitTemplateConfigTest { ".tmp", new File(homeDir)); tempFileInHomeDirectory.deleteOnExit(); JGitTestUtil.write(tempFileInHomeDirectory, templateContent); - String expectedTemplatePath = tempFileInHomeDirectory.getPath() - .replace(homeDir, "~"); + String expectedTemplatePath = "~/" + tempFileInHomeDirectory.getName(); config = ConfigTest .parse("[commit]\n\ttemplate = " + expectedTemplatePath + "\n"); String templatePath = config.get(CommitConfig.KEY) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java index 9b82c2afd6..a85a4f49b6 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java @@ -1488,7 +1488,8 @@ public class ConfigTest { String expectedTemplatePath = tempFile.getPath(); Config config = parse( - "[commit]\n\ttemplate = " + expectedTemplatePath + "\n"); + "[commit]\n\ttemplate = " + + Config.escapeValue(expectedTemplatePath) + "\n"); String templatePath = config.get(CommitConfig.KEY) .getCommitTemplatePath(); @@ -1537,7 +1538,8 @@ public class ConfigTest { JGitTestUtil.write(tempFile, templateContent); String expectedTemplatePath = tempFile.getPath(); config = parse("[i18n]\n\tcommitEncoding = utf-8\n" - + "[commit]\n\ttemplate = " + expectedTemplatePath + "\n"); + + "[commit]\n\ttemplate = " + + Config.escapeValue(expectedTemplatePath) + "\n"); assertEquals(templateContent, config.get(CommitConfig.KEY).getCommitTemplateContent(repo)); String commitEncoding = config.get(CommitConfig.KEY) @@ -1556,7 +1558,8 @@ public class ConfigTest { String templateContent = "content of the template"; JGitTestUtil.write(tempFile, templateContent); config = parse("[i18n]\n\tcommitEncoding = invalidEcoding\n" - + "[commit]\n\ttemplate = " + tempFile.getPath() + "\n"); + + "[commit]\n\ttemplate = " + + Config.escapeValue(tempFile.getPath()) + "\n"); config.get(CommitConfig.KEY).getCommitTemplateContent(repo); } @@ -1570,7 +1573,7 @@ public class ConfigTest { String templateContent = "content of the template"; JGitTestUtil.write(tempFile, templateContent); // commit message encoding - String expectedTemplatePath = "/nonExistingTemplate"; + String expectedTemplatePath = "~/nonExistingTemplate"; config = parse("[commit]\n\ttemplate = " + expectedTemplatePath + "\n"); String templatePath = config.get(CommitConfig.KEY) .getCommitTemplatePath(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java index af8a58f6f0..0fafcd6a36 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java @@ -2,7 +2,7 @@ * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com> * Copyright (C) 2008-2011, Shawn O. Pearce <spearce@spearce.org> * Copyright (C) 2008-2011, Robin Rosenberg <robin.rosenberg@dewire.com> - * Copyright (C) 2010, 2020 Christian Halstrick <christian.halstrick@sap.com> and others + * Copyright (C) 2010, 2022 Christian Halstrick <christian.halstrick@sap.com> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -242,6 +242,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { ListenerHandle handle = null; try (Git git = new Git(db); TestRepository<Repository> db_t = new TestRepository<>(db)) { + db.incrementOpen(); handle = db.getListenerList() .addWorkingTreeModifiedListener(recorder); BranchBuilder master = db_t.branch("master"); @@ -261,6 +262,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { String attributes) throws Exception { try (Git git = new Git(db); TestRepository<Repository> db_t = new TestRepository<>(db)) { + db.incrementOpen(); BranchBuilder master = db_t.branch("master"); master.commit().add("f", inIndex).message("m0").create(); if (!StringUtils.isEmptyOrNull(attributes)) { @@ -313,8 +315,9 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { @Test public void testCheckoutWithLFAuto() throws Exception { - checkoutLineEndings("first line\nsecond line\n", - "first line\nsecond line\n", "f text=auto"); + String expected = String.format("first line%nsecond line%n"); + checkoutLineEndings("first line\nsecond line\n", expected, + "f text=auto"); } @Test @@ -325,9 +328,9 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { @Test public void testCheckoutWithLFAutoEolNative() throws Exception { + String expected = String.format("first line%nsecond line%n"); checkoutLineEndings( - "first line\nsecond line\n", "first line\nsecond line\n" - .replaceAll("\n", System.lineSeparator()), + "first line\nsecond line\n", expected, "f text=auto eol=native"); } @@ -2064,6 +2067,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { public void testCheckoutWithEmptyIndexDoesntOverwrite() throws Exception { try (Git git = new Git(db); TestRepository<Repository> db_t = new TestRepository<>(db)) { + db.incrementOpen(); // prepare the commits BranchBuilder master = db_t.branch("master"); RevCommit mergeCommit = master.commit() diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_PersonIdentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PersonIdentTest.java index e9bab7c4ad..97da1757e0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_PersonIdentTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PersonIdentTest.java @@ -13,12 +13,14 @@ package org.eclipse.jgit.lib; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.time.Instant; +import java.time.ZoneId; import java.util.Date; import java.util.TimeZone; import org.junit.Test; -public class T0001_PersonIdentTest { +public class PersonIdentTest { @Test public void test001_NewIdent() { @@ -42,6 +44,34 @@ public class T0001_PersonIdentTest { p.toExternalString()); } + @Test + public void testNewIdentInstant() { + PersonIdent p = new PersonIdent("A U Thor", "author@example.com", + Instant.ofEpochMilli(1142878501000L), + ZoneId.of("America/New_York")); + assertEquals("A U Thor", p.getName()); + assertEquals("author@example.com", p.getEmailAddress()); + assertEquals(Instant.ofEpochMilli(1142878501000L), + p.getWhenAsInstant()); + assertEquals("A U Thor <author@example.com> 1142878501 -0500", + p.toExternalString()); + assertEquals(ZoneId.of("GMT-05:00"), p.getZoneId()); + } + + @Test + public void testNewIdentInstant2() { + final PersonIdent p = new PersonIdent("A U Thor", "author@example.com", + Instant.ofEpochMilli(1142878501000L), + ZoneId.of("Asia/Kolkata")); + assertEquals("A U Thor", p.getName()); + assertEquals("author@example.com", p.getEmailAddress()); + assertEquals(Instant.ofEpochMilli(1142878501000L), + p.getWhenAsInstant()); + assertEquals("A U Thor <author@example.com> 1142878501 +0530", + p.toExternalString()); + assertEquals(ZoneId.of("GMT+05:30"), p.getZoneId()); + } + @SuppressWarnings("unused") @Test(expected = IllegalArgumentException.class) public void nullForNameShouldThrowIllegalArgumentException() { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java index dedb56c7b0..a2576cc677 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java @@ -13,6 +13,7 @@ import static org.junit.Assert.assertEquals; import java.io.IOException; import java.util.Arrays; +import java.util.List; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; @@ -157,8 +158,8 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase { public void testFormatWithConflictsNoFooter() { String originalMessage = "Header Line\n\nCommit body\n"; String message = formatter.formatWithConflicts(originalMessage, - Arrays.asList(new String[] { "path1" })); - assertEquals("Header Line\n\nCommit body\n\nConflicts:\n\tpath1\n", + List.of("path1"), '#'); + assertEquals("Header Line\n\nCommit body\n\n# Conflicts:\n#\tpath1\n", message); } @@ -166,8 +167,17 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase { public void testFormatWithConflictsNoFooterNoLineBreak() { String originalMessage = "Header Line\n\nCommit body"; String message = formatter.formatWithConflicts(originalMessage, - Arrays.asList(new String[] { "path1" })); - assertEquals("Header Line\n\nCommit body\n\nConflicts:\n\tpath1\n", + List.of("path1"), '#'); + assertEquals("Header Line\n\nCommit body\n\n# Conflicts:\n#\tpath1\n", + message); + } + + @Test + public void testFormatWithConflictsCustomCharacter() { + String originalMessage = "Header Line\n\nCommit body"; + String message = formatter.formatWithConflicts(originalMessage, + List.of("path1"), ';'); + assertEquals("Header Line\n\nCommit body\n\n; Conflicts:\n;\tpath1\n", message); } @@ -176,9 +186,9 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase { String originalMessage = "Header Line\n\nCommit body\n\nChangeId:" + " I123456789123456789123456789123456789\nBug:1234567\n"; String message = formatter.formatWithConflicts(originalMessage, - Arrays.asList(new String[] { "path1" })); + List.of("path1"), '#'); assertEquals( - "Header Line\n\nCommit body\n\nConflicts:\n\tpath1\n\n" + "Header Line\n\nCommit body\n\n# Conflicts:\n#\tpath1\n\n" + "ChangeId: I123456789123456789123456789123456789\nBug:1234567\n", message); } @@ -188,9 +198,9 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase { String originalMessage = "Header Line\n\nCommit body\nBug:1234567\nMore Body\n\nChangeId:" + " I123456789123456789123456789123456789\nBug:1234567\n"; String message = formatter.formatWithConflicts(originalMessage, - Arrays.asList(new String[] { "path1" })); + List.of("path1"), '#'); assertEquals( - "Header Line\n\nCommit body\nBug:1234567\nMore Body\n\nConflicts:\n\tpath1\n\n" + "Header Line\n\nCommit body\nBug:1234567\nMore Body\n\n# Conflicts:\n#\tpath1\n\n" + "ChangeId: I123456789123456789123456789123456789\nBug:1234567\n", message); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java index dd8573d2bc..cbacaed728 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java @@ -1810,6 +1810,7 @@ public class MergerTest extends RepositoryTestCase { private String readBlob(ObjectId treeish, String path) throws Exception { try (TestRepository<?> tr = new TestRepository<>(db); RevWalk rw = tr.getRevWalk()) { + db.incrementOpen(); RevTree tree = rw.parseTree(treeish); RevObject obj = tr.get(tree, path); if (obj == null) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java index ea994f06aa..f446d07513 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java @@ -217,7 +217,6 @@ public class SubmoduleWalkTest extends RepositoryTestCase { assertEqualsFile(modulesGitDir, subRepo.getDirectory()); assertEqualsFile(new File(db.getWorkTree(), path), subRepo.getWorkTree()); - subRepo.close(); assertFalse(gen.next()); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java index 054eb9c5ad..bb62a0d892 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java @@ -80,6 +80,7 @@ public class BundleWriterTest extends SampleDataRepositoryTestCase { // Then we clone a new repo from that bundle and do a simple test. This // makes sure we could read the bundle we created. Repository newRepo = createBareRepository(); + addRepoToClose(newRepo); FetchResult fetchResult = fetchFromBundle(newRepo, bundle); Ref advertisedRef = fetchResult .getAdvertisedRef("refs/heads/firstcommit"); @@ -116,6 +117,7 @@ public class BundleWriterTest extends SampleDataRepositoryTestCase { // makes sure // we could read the bundle we created. Repository newRepo = createBareRepository(); + addRepoToClose(newRepo); FetchResult fetchResult = fetchFromBundle(newRepo, bundle); Ref advertisedRef = fetchResult.getAdvertisedRef("refs/heads/aa"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java index 60b8098b31..93bedb3c97 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java @@ -110,6 +110,7 @@ public class PackParserTest extends RepositoryTestCase { public void testTinyThinPack() throws Exception { RevBlob a; try (TestRepository d = new TestRepository<Repository>(db)) { + db.incrementOpen(); a = d.blob("a"); } @@ -132,6 +133,7 @@ public class PackParserTest extends RepositoryTestCase { public void testPackWithDuplicateBlob() throws Exception { final byte[] data = Constants.encode("0123456789abcdefg"); try (TestRepository<Repository> d = new TestRepository<>(db)) { + db.incrementOpen(); assertTrue(db.getObjectDatabase().has(d.blob(data))); } @@ -151,6 +153,7 @@ public class PackParserTest extends RepositoryTestCase { public void testPackWithTrailingGarbage() throws Exception { RevBlob a; try (TestRepository d = new TestRepository<Repository>(db)) { + db.incrementOpen(); a = d.blob("a"); } @@ -180,6 +183,7 @@ public class PackParserTest extends RepositoryTestCase { public void testMaxObjectSizeFullBlob() throws Exception { final byte[] data = Constants.encode("0123456789"); try (TestRepository d = new TestRepository<Repository>(db)) { + db.incrementOpen(); d.blob(data); } @@ -213,6 +217,7 @@ public class PackParserTest extends RepositoryTestCase { public void testMaxObjectSizeDeltaBlock() throws Exception { RevBlob a; try (TestRepository d = new TestRepository<Repository>(db)) { + db.incrementOpen(); a = d.blob("a"); } @@ -246,6 +251,7 @@ public class PackParserTest extends RepositoryTestCase { public void testMaxObjectSizeDeltaResultSize() throws Exception { RevBlob a; try (TestRepository d = new TestRepository<Repository>(db)) { + db.incrementOpen(); a = d.blob("0123456789"); } @@ -278,6 +284,7 @@ public class PackParserTest extends RepositoryTestCase { public void testNonMarkingInputStream() throws Exception { RevBlob a; try (TestRepository d = new TestRepository<Repository>(db)) { + db.incrementOpen(); a = d.blob("a"); } @@ -318,6 +325,7 @@ public class PackParserTest extends RepositoryTestCase { public void testDataAfterPackFooterSingleRead() throws Exception { RevBlob a; try (TestRepository d = new TestRepository<Repository>(db)) { + db.incrementOpen(); a = d.blob("a"); } @@ -379,6 +387,7 @@ public class PackParserTest extends RepositoryTestCase { final byte[] data = Constants.encode("a"); RevBlob b; try (TestRepository d = new TestRepository<Repository>(db)) { + db.incrementOpen(); b = d.blob(data); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConfigTest.java index 6109d6cb4d..cbc1d546ac 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConfigTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017, David Pursehouse <david.pursehouse@gmail.com> and others + * Copyright (C) 2017, 2022 David Pursehouse <david.pursehouse@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 @@ -14,10 +14,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.transport.PushConfig.PushDefault; import org.eclipse.jgit.transport.PushConfig.PushRecurseSubmodulesMode; import org.junit.Test; public class PushConfigTest { + @Test public void pushRecurseSubmoduleMatch() throws Exception { assertTrue(PushRecurseSubmodulesMode.CHECK.matchConfigValue("check")); @@ -52,4 +55,59 @@ public class PushConfigTest { assertEquals("check", PushRecurseSubmodulesMode.CHECK.toConfigValue()); assertEquals("false", PushRecurseSubmodulesMode.NO.toConfigValue()); } + + @Test + public void pushDefaultMatch() throws Exception { + assertTrue(PushDefault.NOTHING.matchConfigValue("nothing")); + assertTrue(PushDefault.NOTHING.matchConfigValue("NOTHING")); + assertTrue(PushDefault.CURRENT.matchConfigValue("current")); + assertTrue(PushDefault.CURRENT.matchConfigValue("CURRENT")); + assertTrue(PushDefault.UPSTREAM.matchConfigValue("upstream")); + assertTrue(PushDefault.UPSTREAM.matchConfigValue("UPSTREAM")); + assertTrue(PushDefault.UPSTREAM.matchConfigValue("tracking")); + assertTrue(PushDefault.UPSTREAM.matchConfigValue("TRACKING")); + assertTrue(PushDefault.SIMPLE.matchConfigValue("simple")); + assertTrue(PushDefault.SIMPLE.matchConfigValue("SIMPLE")); + assertTrue(PushDefault.MATCHING.matchConfigValue("matching")); + assertTrue(PushDefault.MATCHING.matchConfigValue("MATCHING")); + } + + @Test + public void pushDefaultNoMatch() throws Exception { + assertFalse(PushDefault.NOTHING.matchConfigValue("n")); + assertFalse(PushDefault.CURRENT.matchConfigValue("")); + assertFalse(PushDefault.UPSTREAM.matchConfigValue("track")); + } + + @Test + public void pushDefaultToConfigValue() throws Exception { + assertEquals("nothing", PushDefault.NOTHING.toConfigValue()); + assertEquals("current", PushDefault.CURRENT.toConfigValue()); + assertEquals("upstream", PushDefault.UPSTREAM.toConfigValue()); + assertEquals("simple", PushDefault.SIMPLE.toConfigValue()); + assertEquals("matching", PushDefault.MATCHING.toConfigValue()); + } + + @Test + public void testEmptyConfig() throws Exception { + PushConfig cfg = parse(""); + assertEquals(PushRecurseSubmodulesMode.NO, cfg.getRecurseSubmodules()); + assertEquals(PushDefault.SIMPLE, cfg.getPushDefault()); + } + + @Test + public void testConfig() throws Exception { + PushConfig cfg = parse( + "[push]\n\tdefault = tracking\n\trecurseSubmodules = on-demand\n"); + assertEquals(PushRecurseSubmodulesMode.ON_DEMAND, + cfg.getRecurseSubmodules()); + assertEquals(PushDefault.UPSTREAM, cfg.getPushDefault()); + } + + private static PushConfig parse(String content) throws Exception { + Config c = new Config(); + c.fromText(content); + return c.get(PushConfig::new); + } + } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java index 6928859622..2e8b30f151 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java @@ -14,14 +14,19 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import org.eclipse.jgit.api.errors.AbortedByHookException; import org.eclipse.jgit.errors.NotSupportedException; import org.eclipse.jgit.errors.TransportException; +import org.eclipse.jgit.hooks.PrePushHook; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; import org.eclipse.jgit.lib.ProgressMonitor; @@ -31,6 +36,7 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.TextProgressMonitor; import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase; import org.eclipse.jgit.transport.RemoteRefUpdate.Status; +import org.eclipse.jgit.util.io.NullOutputStream; import org.junit.Before; import org.junit.Test; @@ -220,7 +226,17 @@ public class PushProcessTest extends SampleDataRepositoryTestCase { .fromString("0000000000000000000000000000000000000001")); final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master", ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef")); - testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null); + try (ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(bytes, true, + StandardCharsets.UTF_8); + PrintStream err = new PrintStream(NullOutputStream.INSTANCE)) { + MockPrePushHook hook = new MockPrePushHook(db, out, err); + testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null, + hook); + out.flush(); + String result = new String(bytes.toString(StandardCharsets.UTF_8)); + assertEquals("", result); + } } /** @@ -256,10 +272,22 @@ public class PushProcessTest extends SampleDataRepositoryTestCase { refUpdates.add(rruOk); refUpdates.add(rruReject); advertisedRefs.add(refToChange); - executePush(); - assertEquals(Status.OK, rruOk.getStatus()); - assertTrue(rruOk.isFastForward()); - assertEquals(Status.NON_EXISTING, rruReject.getStatus()); + try (ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(bytes, true, + StandardCharsets.UTF_8); + PrintStream err = new PrintStream(NullOutputStream.INSTANCE)) { + MockPrePushHook hook = new MockPrePushHook(db, out, err); + executePush(hook); + assertEquals(Status.OK, rruOk.getStatus()); + assertTrue(rruOk.isFastForward()); + assertEquals(Status.NON_EXISTING, rruReject.getStatus()); + out.flush(); + String result = new String(bytes.toString(StandardCharsets.UTF_8)); + assertEquals( + "null 0000000000000000000000000000000000000000 " + + "refs/heads/master 2c349335b7f797072cf729c4f3bb0914ecb6dec9\n", + result); + } } /** @@ -346,10 +374,18 @@ public class PushProcessTest extends SampleDataRepositoryTestCase { final Ref advertisedRef, final Status expectedStatus, Boolean fastForward) throws NotSupportedException, TransportException { + return testOneUpdateStatus(rru, advertisedRef, expectedStatus, + fastForward, null); + } + + private PushResult testOneUpdateStatus(final RemoteRefUpdate rru, + final Ref advertisedRef, final Status expectedStatus, + Boolean fastForward, PrePushHook hook) + throws NotSupportedException, TransportException { refUpdates.add(rru); if (advertisedRef != null) advertisedRefs.add(advertisedRef); - final PushResult result = executePush(); + final PushResult result = executePush(hook); assertEquals(expectedStatus, rru.getStatus()); if (fastForward != null) assertEquals(fastForward, Boolean.valueOf(rru.isFastForward())); @@ -358,7 +394,12 @@ public class PushProcessTest extends SampleDataRepositoryTestCase { private PushResult executePush() throws NotSupportedException, TransportException { - process = new PushProcess(transport, refUpdates); + return executePush(null); + } + + private PushResult executePush(PrePushHook hook) + throws NotSupportedException, TransportException { + process = new PushProcess(transport, refUpdates, hook); return process.execute(new TextProgressMonitor()); } @@ -416,4 +457,20 @@ public class PushProcessTest extends SampleDataRepositoryTestCase { } } } + + private static class MockPrePushHook extends PrePushHook { + + private final PrintStream output; + + public MockPrePushHook(Repository repo, PrintStream out, + PrintStream err) { + super(repo, out, err); + output = out; + } + + @Override + protected void doRun() throws AbortedByHookException, IOException { + output.print(getStdinArgs()); + } + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java index d1e5446827..a91bc95c8d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java @@ -73,11 +73,14 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas super.setUp(); src = createBareRepository(); + addRepoToClose(src); dst = createBareRepository(); + addRepoToClose(dst); // Fill dst with a some common history. // try (TestRepository<Repository> d = new TestRepository<>(dst)) { + dst.incrementOpen(); a = d.blob("a"); A = d.commit(d.tree(d.file("a", a))); B = d.commit().parent(A).create(); @@ -106,9 +109,6 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas dst.getDirectory()) { @Override ReceivePack createReceivePack(Repository db) { - db.close(); - dst.incrementOpen(); - final ReceivePack rp = super.createReceivePack(dst); rp.setAdvertiseRefsHook(new HidePrivateHook()); return rp; @@ -136,8 +136,6 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas dst.getDirectory()) { @Override ReceivePack createReceivePack(Repository db) { - dst.incrementOpen(); - ReceivePack rp = super.createReceivePack(dst); rp.setAdvertiseRefsHook(new AdvertiseRefsHook() { @Override @@ -173,9 +171,6 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas return new TransportLocal(src, uriOf(dst), dst.getDirectory()) { @Override ReceivePack createReceivePack(Repository db) { - db.close(); - dst.incrementOpen(); - final ReceivePack rp = super.createReceivePack(dst); rp.setCheckReceivedObjects(true); rp.setCheckReferencedObjectsAreReachable(true); @@ -211,6 +206,7 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas // Now use b but in a different commit than what is hidden. // try (TestRepository<Repository> s = new TestRepository<>(src)) { + src.incrementOpen(); RevCommit N = s.commit().parent(B).add("q", b).create(); s.update(R_MASTER, N); @@ -228,7 +224,6 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas try (TransportLocal t = newTransportLocalWithStrictValidation()) { t.setPushThin(true); r = t.push(PM, Collections.singleton(u)); - dst.close(); } assertNotNull("have result", r); @@ -290,6 +285,7 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas public void testUsingHiddenDeltaBaseFails() throws Exception { byte[] delta = { 0x1, 0x1, 0x1, 'c' }; try (TestRepository<Repository> s = new TestRepository<>(src)) { + src.incrementOpen(); RevCommit N = s.commit().parent(B) .add("q", s.blob(BinaryDelta.apply( @@ -348,6 +344,7 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas // Try to use the 'b' blob that is hidden. // try (TestRepository<Repository> s = new TestRepository<>(src)) { + src.incrementOpen(); RevCommit N = s.commit().parent(B).add("q", s.blob("b")).create(); // But don't include it in the pack. @@ -401,6 +398,7 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas // Try to use the 'n' blob that is not on the server. // try (TestRepository<Repository> s = new TestRepository<>(src)) { + src.incrementOpen(); RevBlob n = s.blob("n"); RevCommit N = s.commit().parent(B).add("q", n).create(); @@ -491,6 +489,7 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas .toString(); try (TestRepository<Repository> s = new TestRepository<>(src)) { + src.incrementOpen(); RevBlob blob = s.blob(fakeGitmodules); RevCommit N = s.commit().parent(B).add(".gitmodules", blob) .create(); @@ -517,6 +516,7 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas @Test public void testUsingUnknownTreeFails() throws Exception { try (TestRepository<Repository> s = new TestRepository<>(src)) { + src.incrementOpen(); RevCommit N = s.commit().parent(B).add("q", s.blob("a")).create(); RevTree t = s.parseBody(N).getTree(); 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 5569bca23c..b56308cb72 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 @@ -466,4 +466,18 @@ public class RefSpecTest { assertTrue(a.matchSource("refs/heads/master")); assertNull(a.getDestination()); } + + @Test + public void matching() { + RefSpec a = new RefSpec(":"); + assertTrue(a.isMatching()); + assertFalse(a.isForceUpdate()); + } + + @Test + public void matchingForced() { + RefSpec a = new RefSpec("+:"); + assertTrue(a.isMatching()); + assertTrue(a.isForceUpdate()); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FilterCommandsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FilterCommandsTest.java index 36f94fbd20..89d31c3e8f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FilterCommandsTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FilterCommandsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016, Christian Halstrick <christian.halstrick@sap.com> and others + * Copyright (C) 2016, 2022 Christian Halstrick <christian.halstrick@sap.com> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -10,12 +10,17 @@ package org.eclipse.jgit.util; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.HashSet; +import java.util.Set; import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.MergeResult; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.attributes.FilterCommand; import org.eclipse.jgit.attributes.FilterCommandFactory; @@ -86,6 +91,14 @@ public class FilterCommandsTest extends RepositoryTestCase { secondCommit = git.commit().setMessage("Second commit").call(); } + @Override + public void tearDown() throws Exception { + Set<String> existingFilters = new HashSet<>( + FilterCommandRegistry.getRegisteredFilterCommands()); + existingFilters.forEach(FilterCommandRegistry::unregister); + super.tearDown(); + } + @Test public void testBuiltinCleanFilter() throws IOException, GitAPIException { @@ -217,4 +230,133 @@ public class FilterCommandsTest extends RepositoryTestCase { config.save(); } + @Test + public void testBranchSwitch() throws Exception { + String builtinCommandPrefix = "jgit://builtin/test/"; + FilterCommandRegistry.register(builtinCommandPrefix + "smudge", + new TestCommandFactory('s')); + FilterCommandRegistry.register(builtinCommandPrefix + "clean", + new TestCommandFactory('c')); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "test", "smudge", + builtinCommandPrefix + "smudge"); + config.setString("filter", "test", "clean", + builtinCommandPrefix + "clean"); + config.save(); + // We're on the test branch + File aFile = writeTrashFile("a.txt", "a"); + writeTrashFile(".gitattributes", "a.txt filter=test"); + File cFile = writeTrashFile("cc/c.txt", "C"); + writeTrashFile("cc/.gitattributes", "c.txt filter=test"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("On test").call(); + git.checkout().setName("master").call(); + git.branchCreate().setName("other").call(); + git.checkout().setName("other").call(); + writeTrashFile("b.txt", "b"); + writeTrashFile(".gitattributes", "b.txt filter=test"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("On other").call(); + git.checkout().setName("test").call(); + checkFile(aFile, "scsa"); + checkFile(cFile, "scsC"); + } + + @Test + public void testCheckoutSingleFile() throws Exception { + String builtinCommandPrefix = "jgit://builtin/test/"; + FilterCommandRegistry.register(builtinCommandPrefix + "smudge", + new TestCommandFactory('s')); + FilterCommandRegistry.register(builtinCommandPrefix + "clean", + new TestCommandFactory('c')); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "test", "smudge", + builtinCommandPrefix + "smudge"); + config.setString("filter", "test", "clean", + builtinCommandPrefix + "clean"); + config.save(); + // We're on the test branch + File aFile = writeTrashFile("a.txt", "a"); + File attributes = writeTrashFile(".gitattributes", "a.txt filter=test"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("On test").call(); + git.checkout().setName("master").call(); + git.branchCreate().setName("other").call(); + git.checkout().setName("other").call(); + writeTrashFile("b.txt", "b"); + writeTrashFile(".gitattributes", "b.txt filter=test"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("On other").call(); + git.checkout().setName("master").call(); + assertFalse(aFile.exists()); + assertFalse(attributes.exists()); + git.checkout().setStartPoint("test").addPath("a.txt").call(); + checkFile(aFile, "scsa"); + } + + @Test + public void testCheckoutSingleFile2() throws Exception { + String builtinCommandPrefix = "jgit://builtin/test/"; + FilterCommandRegistry.register(builtinCommandPrefix + "smudge", + new TestCommandFactory('s')); + FilterCommandRegistry.register(builtinCommandPrefix + "clean", + new TestCommandFactory('c')); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "test", "smudge", + builtinCommandPrefix + "smudge"); + config.setString("filter", "test", "clean", + builtinCommandPrefix + "clean"); + config.save(); + // We're on the test branch + File aFile = writeTrashFile("a.txt", "a"); + File attributes = writeTrashFile(".gitattributes", "a.txt filter=test"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("On test").call(); + git.checkout().setName("master").call(); + git.branchCreate().setName("other").call(); + git.checkout().setName("other").call(); + writeTrashFile("b.txt", "b"); + writeTrashFile(".gitattributes", "b.txt filter=test"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("On other").call(); + git.checkout().setName("master").call(); + assertFalse(aFile.exists()); + assertFalse(attributes.exists()); + writeTrashFile(".gitattributes", ""); + git.checkout().setStartPoint("test").addPath("a.txt").call(); + checkFile(aFile, "scsa"); + } + + @Test + public void testMerge() throws Exception { + String builtinCommandPrefix = "jgit://builtin/test/"; + FilterCommandRegistry.register(builtinCommandPrefix + "smudge", + new TestCommandFactory('s')); + FilterCommandRegistry.register(builtinCommandPrefix + "clean", + new TestCommandFactory('c')); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "test", "smudge", + builtinCommandPrefix + "smudge"); + config.setString("filter", "test", "clean", + builtinCommandPrefix + "clean"); + config.save(); + // We're on the test branch. Set up two branches that are expected to + // merge cleanly. + File aFile = writeTrashFile("a.txt", "a"); + writeTrashFile(".gitattributes", "a.txt filter=test"); + git.add().addFilepattern(".").call(); + RevCommit aCommit = git.commit().setMessage("On test").call(); + git.checkout().setName("master").call(); + assertFalse(aFile.exists()); + git.branchCreate().setName("other").call(); + git.checkout().setName("other").call(); + writeTrashFile("b/b.txt", "b"); + writeTrashFile("b/.gitattributes", "b.txt filter=test"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("On other").call(); + MergeResult result = git.merge().include(aCommit).call(); + assertEquals(MergeResult.MergeStatus.MERGED, result.getMergeStatus()); + checkFile(aFile, "scsa"); + } + } diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF index 57a8bee99a..7baa215801 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.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-RequiredExecutionEnvironment: JavaSE-11 -Export-Package: org.eclipse.jgit.awtui;version="6.0.1" -Import-Package: org.eclipse.jgit.errors;version="[6.0.1,6.1.0)", - org.eclipse.jgit.lib;version="[6.0.1,6.1.0)", - org.eclipse.jgit.nls;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revplot;version="[6.0.1,6.1.0)", - org.eclipse.jgit.revwalk;version="[6.0.1,6.1.0)", - org.eclipse.jgit.transport;version="[6.0.1,6.1.0)", - org.eclipse.jgit.util;version="[6.0.1,6.1.0)" +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)" diff --git a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF index 335c15766b..75f3d217ab 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.ui;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ui;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml index c4a3e2a587..125e120b45 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.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ui</artifactId> diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters new file mode 100644 index 0000000000..811c3600af --- /dev/null +++ b/org.eclipse.jgit/.settings/.api_filters @@ -0,0 +1,292 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<component id="org.eclipse.jgit" version="2"> + <resource path="src/org/eclipse/jgit/api/GarbageCollectCommand.java" type="org.eclipse.jgit.api.GarbageCollectCommand"> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.3"/> + <message_argument value="setPackKeptObjects(boolean)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/errors/NoRemoteRepositoryException.java" type="org.eclipse.jgit.errors.NoRemoteRepositoryException"> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.1"/> + <message_argument value="NoRemoteRepositoryException(URIish, String, Throwable)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/errors/PackMismatchException.java" type="org.eclipse.jgit.errors.PackMismatchException"> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.9.1"/> + <message_argument value="isPermanent()"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.9.1"/> + <message_argument value="setPermanent(boolean)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/lib/ConfigConstants.java" type="org.eclipse.jgit.lib.ConfigConstants"> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.2"/> + <message_argument value="CONFIG_KEY_BITMAP_EXCLUDED_REFS_PREFIXES"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.2"/> + <message_argument value="CONFIG_KEY_PRESERVE_OLD_PACKS"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.2"/> + <message_argument value="CONFIG_KEY_PRUNE_PRESERVED"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.2"/> + <message_argument value="CONFIG_KEY_SKIPHASH"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.2"/> + <message_argument value="SHA1_IMPLEMENTATION"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.3"/> + <message_argument value="CONFIG_KEY_PACK_KEPT_OBJECTS"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.3"/> + <message_argument value="CONFIG_REPACK_SECTION"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="6.1.1"/> + <message_argument value="CONFIG_KEY_TRUST_PACKED_REFS_STAT"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/lib/CoreConfig.java" type="org.eclipse.jgit.lib.CoreConfig$TrustPackedRefsStat"> + <filter id="1142947843"> + <message_arguments> + <message_argument value="6.1.1"/> + <message_argument value="TrustPackedRefsStat"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/lib/ObjectDatabase.java" type="org.eclipse.jgit.lib.ObjectDatabase"> + <filter id="336695337"> + <message_arguments> + <message_argument value="org.eclipse.jgit.lib.ObjectDatabase"/> + <message_argument value="getApproximateObjectCount()"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/lib/Repository.java" type="org.eclipse.jgit.lib.Repository"> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.2"/> + <message_argument value="getReflogReader(Ref)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/lib/TypedConfigGetter.java" type="org.eclipse.jgit.lib.TypedConfigGetter"> + <filter id="403767336"> + <message_arguments> + <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/> + <message_argument value="UNSET_INT"/> + </message_arguments> + </filter> + <filter id="403804204"> + <message_arguments> + <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/> + <message_argument value="getIntInRange(Config, String, String, String, int, int, int)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/merge/ResolveMerger.java" type="org.eclipse.jgit.merge.ResolveMerger"> + <filter id="338792546"> + <message_arguments> + <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/> + <message_argument value="addCheckoutMetadata(String, Attributes)"/> + </message_arguments> + </filter> + <filter id="338792546"> + <message_arguments> + <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/> + <message_argument value="addToCheckout(String, DirCacheEntry, Attributes)"/> + </message_arguments> + </filter> + <filter id="338792546"> + <message_arguments> + <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/> + <message_argument value="processEntry(CanonicalTreeParser, CanonicalTreeParser, CanonicalTreeParser, DirCacheBuildIterator, WorkingTreeIterator, boolean, Attributes)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/storage/pack/PackConfig.java" type="org.eclipse.jgit.storage.pack.PackConfig"> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.storage.pack.PackConfig"/> + <message_argument value="DEFAULT_BITMAP_EXCLUDED_REFS_PREFIXES"/> + </message_arguments> + </filter> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.storage.pack.PackConfig"/> + <message_argument value="DEFAULT_PACK_KEPT_OBJECTS"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.2"/> + <message_argument value="DEFAULT_BITMAP_EXCLUDED_REFS_PREFIXES"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.2"/> + <message_argument value="getBitmapExcludedRefsPrefixes()"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.2"/> + <message_argument value="setBitmapExcludedRefsPrefixes(String[])"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.3"/> + <message_argument value="DEFAULT_PACK_KEPT_OBJECTS"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.3"/> + <message_argument value="isPackKeptObjects()"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.3"/> + <message_argument value="setPackKeptObjects(boolean)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/transport/AwsRequestSignerV4.java" type="org.eclipse.jgit.transport.AwsRequestSignerV4"> + <filter id="1109393411"> + <message_arguments> + <message_argument value="5.13.1"/> + <message_argument value="org.eclipse.jgit.transport.AwsRequestSignerV4"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/transport/BasePackPushConnection.java" type="org.eclipse.jgit.transport.BasePackPushConnection"> + <filter id="338792546"> + <message_arguments> + <message_argument value="org.eclipse.jgit.transport.BasePackPushConnection"/> + <message_argument value="noRepository()"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/transport/PushConfig.java" type="org.eclipse.jgit.transport.PushConfig"> + <filter id="338722907"> + <message_arguments> + <message_argument value="org.eclipse.jgit.transport.PushConfig"/> + <message_argument value="PushConfig()"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/util/HttpSupport.java" type="org.eclipse.jgit.util.HttpSupport"> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.1"/> + <message_argument value="urlEncode(String, boolean)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/util/sha1/SHA1.java" type="org.eclipse.jgit.util.sha1.SHA1"> + <filter id="337764418"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.sha1.SHA1"/> + </message_arguments> + </filter> + <filter id="421650549"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.sha1.SHA1"/> + <message_argument value="digest()"/> + </message_arguments> + </filter> + <filter id="421650549"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.sha1.SHA1"/> + <message_argument value="digest(MutableObjectId)"/> + </message_arguments> + </filter> + <filter id="421650549"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.sha1.SHA1"/> + <message_argument value="hasCollision()"/> + </message_arguments> + </filter> + <filter id="421650549"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.sha1.SHA1"/> + <message_argument value="reset()"/> + </message_arguments> + </filter> + <filter id="421650549"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.sha1.SHA1"/> + <message_argument value="setDetectCollision(boolean)"/> + </message_arguments> + </filter> + <filter id="421650549"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.sha1.SHA1"/> + <message_argument value="toObjectId()"/> + </message_arguments> + </filter> + <filter id="421650549"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.sha1.SHA1"/> + <message_argument value="update(byte)"/> + </message_arguments> + </filter> + <filter id="421650549"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.sha1.SHA1"/> + <message_argument value="update(byte[])"/> + </message_arguments> + </filter> + <filter id="421650549"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.sha1.SHA1"/> + <message_argument value="update(byte[], int, int)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/util/sha1/SHA1.java" type="org.eclipse.jgit.util.sha1.SHA1$Sha1Implementation"> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.13.2"/> + <message_argument value="Sha1Implementation"/> + </message_arguments> + </filter> + </resource> +</component> diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index 87b58e1fc5..808d5d37b3 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.0.1.qualifier +Bundle-Version: 6.1.1.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor Eclipse-ExtensibleAPI: true -Export-Package: org.eclipse.jgit.annotations;version="6.0.1", - org.eclipse.jgit.api;version="6.0.1"; +Export-Package: org.eclipse.jgit.annotations;version="6.1.1", + org.eclipse.jgit.api;version="6.1.1"; 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.0.1", org.eclipse.jgit.revwalk.filter, org.eclipse.jgit.blame, org.eclipse.jgit.merge", - org.eclipse.jgit.api.errors;version="6.0.1"; + org.eclipse.jgit.api.errors;version="6.1.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.errors", - org.eclipse.jgit.attributes;version="6.0.1"; + org.eclipse.jgit.attributes;version="6.1.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk", - org.eclipse.jgit.blame;version="6.0.1"; + org.eclipse.jgit.blame;version="6.1.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff", - org.eclipse.jgit.diff;version="6.0.1"; + org.eclipse.jgit.diff;version="6.1.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.attributes, org.eclipse.jgit.revwalk, @@ -42,44 +42,48 @@ Export-Package: org.eclipse.jgit.annotations;version="6.0.1", org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.treewalk, org.eclipse.jgit.util", - org.eclipse.jgit.dircache;version="6.0.1"; + org.eclipse.jgit.dircache;version="6.1.1"; 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.0.1"; + org.eclipse.jgit.errors;version="6.1.1"; 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.0.1"; + org.eclipse.jgit.events;version="6.1.1"; uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.fnmatch;version="6.0.1", - org.eclipse.jgit.gitrepo;version="6.0.1"; + org.eclipse.jgit.fnmatch;version="6.1.1", + org.eclipse.jgit.gitrepo;version="6.1.1"; 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.0.1";x-internal:=true, - org.eclipse.jgit.hooks;version="6.0.1";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.ignore;version="6.0.1", - org.eclipse.jgit.ignore.internal;version="6.0.1"; + 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"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal;version="6.0.1"; + org.eclipse.jgit.internal;version="6.1.1"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.http.test", - org.eclipse.jgit.internal.fsck;version="6.0.1"; + org.eclipse.jgit.internal.diffmergetool;version="6.1.1"; + x-friends:="org.eclipse.jgit.test, + org.eclipse.jgit.pgm.test, + org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.fsck;version="6.1.1"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.revwalk;version="6.0.1"; + org.eclipse.jgit.internal.revwalk;version="6.1.1"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.storage.dfs;version="6.0.1"; + org.eclipse.jgit.internal.storage.dfs;version="6.1.1"; 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.0.1"; + org.eclipse.jgit.internal.storage.file;version="6.1.1"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.junit, org.eclipse.jgit.junit.http, @@ -88,32 +92,32 @@ Export-Package: org.eclipse.jgit.annotations;version="6.0.1", org.eclipse.jgit.pgm, org.eclipse.jgit.pgm.test, org.eclipse.jgit.ssh.apache", - org.eclipse.jgit.internal.storage.io;version="6.0.1"; + org.eclipse.jgit.internal.storage.io;version="6.1.1"; x-friends:="org.eclipse.jgit.junit, org.eclipse.jgit.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.pack;version="6.0.1"; + org.eclipse.jgit.internal.storage.pack;version="6.1.1"; x-friends:="org.eclipse.jgit.junit, org.eclipse.jgit.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.reftable;version="6.0.1"; + org.eclipse.jgit.internal.storage.reftable;version="6.1.1"; 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.0.1";x-internal:=true, - org.eclipse.jgit.internal.transport.connectivity;version="6.0.1"; + org.eclipse.jgit.internal.submodule;version="6.1.1";x-internal:=true, + org.eclipse.jgit.internal.transport.connectivity;version="6.1.1"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.http;version="6.0.1"; + org.eclipse.jgit.internal.transport.http;version="6.1.1"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.parser;version="6.0.1"; + org.eclipse.jgit.internal.transport.parser;version="6.1.1"; x-friends:="org.eclipse.jgit.http.server, org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.ssh;version="6.0.1"; + org.eclipse.jgit.internal.transport.ssh;version="6.1.1"; x-friends:="org.eclipse.jgit.ssh.apache, org.eclipse.jgit.ssh.jsch, org.eclipse.jgit.test", - org.eclipse.jgit.lib;version="6.0.1"; + org.eclipse.jgit.lib;version="6.1.1"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util.sha1, org.eclipse.jgit.dircache, @@ -127,10 +131,11 @@ Export-Package: org.eclipse.jgit.annotations;version="6.0.1", org.eclipse.jgit.util, org.eclipse.jgit.submodule, org.eclipse.jgit.util.time", - org.eclipse.jgit.lib.internal;version="6.0.1"; - x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.logging;version="6.0.1", - org.eclipse.jgit.merge;version="6.0.1"; + org.eclipse.jgit.lib.internal;version="6.1.1"; + 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"; uses:="org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, @@ -139,40 +144,40 @@ Export-Package: org.eclipse.jgit.annotations;version="6.0.1", org.eclipse.jgit.util, org.eclipse.jgit.api, org.eclipse.jgit.attributes", - org.eclipse.jgit.nls;version="6.0.1", - org.eclipse.jgit.notes;version="6.0.1"; + org.eclipse.jgit.nls;version="6.1.1", + org.eclipse.jgit.notes;version="6.1.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk, org.eclipse.jgit.merge", - org.eclipse.jgit.patch;version="6.0.1"; + org.eclipse.jgit.patch;version="6.1.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.diff", - org.eclipse.jgit.revplot;version="6.0.1"; + org.eclipse.jgit.revplot;version="6.1.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk", - org.eclipse.jgit.revwalk;version="6.0.1"; + org.eclipse.jgit.revwalk;version="6.1.1"; 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.0.1"; + org.eclipse.jgit.revwalk.filter;version="6.1.1"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.lib, org.eclipse.jgit.util", - org.eclipse.jgit.storage.file;version="6.0.1"; + org.eclipse.jgit.storage.file;version="6.1.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.util", - org.eclipse.jgit.storage.pack;version="6.0.1"; + org.eclipse.jgit.storage.pack;version="6.1.1"; uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.submodule;version="6.0.1"; + org.eclipse.jgit.submodule;version="6.1.1"; 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.0.1"; + org.eclipse.jgit.transport;version="6.1.1"; uses:="javax.crypto, org.eclipse.jgit.util.io, org.eclipse.jgit.lib, @@ -185,21 +190,21 @@ Export-Package: org.eclipse.jgit.annotations;version="6.0.1", org.eclipse.jgit.transport.resolver, org.eclipse.jgit.storage.pack, org.eclipse.jgit.errors", - org.eclipse.jgit.transport.http;version="6.0.1"; + org.eclipse.jgit.transport.http;version="6.1.1"; uses:="javax.net.ssl", - org.eclipse.jgit.transport.resolver;version="6.0.1"; + org.eclipse.jgit.transport.resolver;version="6.1.1"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.lib", - org.eclipse.jgit.treewalk;version="6.0.1"; + org.eclipse.jgit.treewalk;version="6.1.1"; 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.0.1"; + org.eclipse.jgit.treewalk.filter;version="6.1.1"; uses:="org.eclipse.jgit.treewalk", - org.eclipse.jgit.util;version="6.0.1"; + org.eclipse.jgit.util;version="6.1.1"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.hooks, org.eclipse.jgit.revwalk, @@ -212,12 +217,12 @@ Export-Package: org.eclipse.jgit.annotations;version="6.0.1", org.eclipse.jgit.treewalk, javax.net.ssl, org.eclipse.jgit.util.time", - org.eclipse.jgit.util.io;version="6.0.1"; + org.eclipse.jgit.util.io;version="6.1.1"; uses:="org.eclipse.jgit.attributes, org.eclipse.jgit.lib, org.eclipse.jgit.treewalk", - org.eclipse.jgit.util.sha1;version="6.0.1", - org.eclipse.jgit.util.time;version="6.0.1" + org.eclipse.jgit.util.sha1;version="6.1.1", + org.eclipse.jgit.util.time;version="6.1.1" 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 302c4548a3..33e6156773 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.0.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit;version="6.0.1.qualifier";roots="." +Bundle-Version: 6.1.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit;version="6.1.1.qualifier";roots="." diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index e78227250f..f0e92b3894 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.0.1-SNAPSHOT</version> + <version>6.1.1-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 c887c6dfbf..5534a0a44c 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -359,6 +359,8 @@ initFailedNonBareRepoSameDirs=When initializing a non-bare repo with directory { inMemoryBufferLimitExceeded=In-memory buffer limit exceeded inputDidntMatchLength=Input did not match supplied length. {0} bytes are missing. inputStreamMustSupportMark=InputStream must support mark() +integerValueNotInRange=Integer value {0}.{1} = {2} not in range {3}..{4} +integerValueNotInRangeSubSection=Integer value {0}.{1}.{2} = {3} not in range {4}..{5} integerValueOutOfRange=Integer value {0}.{1} out of range internalRevisionError=internal revision error internalServerError=internal server error @@ -370,6 +372,7 @@ invalidAwsApiSignatureVersion=Invalid aws.api.signature.version: {0} invalidBooleanValue=Invalid boolean value: {0}.{1}={2} invalidChannel=Invalid channel {0} invalidCommitParentNumber=Invalid commit parent number +invalidCoreAbbrev=Invalid value {0} of option core.abbrev invalidDepth=Invalid depth: {0} invalidEncoding=Invalid encoding from git config i18n.commitEncoding: {0} invalidEncryption=Invalid encryption @@ -571,6 +574,11 @@ pushCertificateInvalidField=Push certificate has missing or invalid value for {0 pushCertificateInvalidFieldValue=Push certificate has missing or invalid value for {0}: {1} pushCertificateInvalidHeader=Push certificate has invalid header format pushCertificateInvalidSignature=Push certificate has invalid signature format +pushDefaultNothing=No refspec given and push.default=nothing; no upstream branch can be determined +pushDefaultNoUpstream=No upstream branch found for local branch ''{0}'' +pushDefaultSimple=push.default=simple requires local branch name ''{0}'' to be equal to upstream tracked branch name ''{1}'' +pushDefaultTriangularUpstream=push.default=upstream cannot be used when the push remote ''{0}'' is different from the fetch remote ''{1}'' +pushDefaultUnknown=Unknown push.default={0}; cannot push pushIsNotSupportedForBundleTransport=Push is not supported for bundle transport pushNotPermitted=push not permitted pushOptionsNotSupported=Push options not supported; received {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 7922f9e729..f88179ac1a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java @@ -9,6 +9,8 @@ */ package org.eclipse.jgit.api; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH; + import java.io.IOException; import java.text.MessageFormat; import java.util.LinkedList; @@ -124,7 +126,7 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> { final RevCommit srcParent = getParentCommit(srcCommit, revWalk); String ourName = calculateOurName(headRef); - String cherryPickName = srcCommit.getId().abbreviate(7).name() + String cherryPickName = srcCommit.getId().abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH).name() + " " + srcCommit.getShortMessage(); //$NON-NLS-1$ Merger merger = strategy.newMerger(repo); @@ -183,7 +185,7 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> { if (unmergedPaths != null) { message = new MergeMessageFormatter() .formatWithConflicts(srcCommit.getFullMessage(), - unmergedPaths); + unmergedPaths, '#'); } 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 37f1d482aa..7a591aa3b5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.api.errors.AbortedByHookException; import org.eclipse.jgit.api.errors.CanceledException; import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; @@ -46,6 +47,8 @@ import org.eclipse.jgit.hooks.PostCommitHook; import org.eclipse.jgit.hooks.PreCommitHook; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.CommitBuilder; +import org.eclipse.jgit.lib.CommitConfig; +import org.eclipse.jgit.lib.CommitConfig.CleanupMode; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.GpgConfig; @@ -133,6 +136,12 @@ public class CommitCommand extends GitCommand<RevCommit> { private CredentialsProvider credentialsProvider; + private @NonNull CleanupMode cleanupMode = CleanupMode.VERBATIM; + + private boolean cleanDefaultIsStrip = true; + + private Character commentChar; + /** * Constructor for CommitCommand * @@ -200,7 +209,7 @@ public class CommitCommand extends GitCommand<RevCommit> { throw new WrongRepositoryStateException( JGitText.get().commitAmendOnInitialNotPossible); - if (headId != null) + if (headId != null) { if (amend) { RevCommit previousCommit = rw.parseCommit(headId); for (RevCommit p : previousCommit.getParents()) @@ -210,7 +219,7 @@ public class CommitCommand extends GitCommand<RevCommit> { } else { parents.add(0, headId); } - + } if (!noVerify) { message = Hooks .commitMsg(repo, @@ -219,6 +228,19 @@ public class CommitCommand extends GitCommand<RevCommit> { .setCommitMessage(message).call(); } + CommitConfig config = null; + if (CleanupMode.DEFAULT.equals(cleanupMode)) { + 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(); + } + message = CommitConfig.cleanText(message, cleanupMode, comments); + RevCommit revCommit; DirCache index = repo.lockDirCache(); try (ObjectInserter odi = repo.newObjectInserter()) { @@ -658,6 +680,57 @@ public class CommitCommand extends GitCommand<RevCommit> { } /** + * Sets the {@link CleanupMode} to apply to the commit message. If not + * called, {@link CommitCommand} applies {@link CleanupMode#VERBATIM}. + * + * @param mode + * {@link CleanupMode} to set + * @return {@code this} + * @since 6.1 + */ + public CommitCommand setCleanupMode(@NonNull CleanupMode mode) { + checkCallable(); + this.cleanupMode = mode; + return this; + } + + /** + * Sets the default clean mode if {@link #setCleanupMode(CleanupMode) + * setCleanupMode(CleanupMode.DEFAULT)} is set and git config + * {@code commit.cleanup = default} or is not set. + * + * @param strip + * if {@code true}, default to {@link CleanupMode#STRIP}; + * otherwise default to {@link CleanupMode#WHITESPACE} + * @return {@code this} + * @since 6.1 + */ + public CommitCommand setDefaultClean(boolean strip) { + checkCallable(); + this.cleanDefaultIsStrip = strip; + return this; + } + + /** + * Sets the comment character to apply when cleaning a commit message. If + * {@code null} (the default) and the {@link #setCleanupMode(CleanupMode) + * clean-up mode} is {@link CleanupMode#STRIP} or + * {@link CleanupMode#SCISSORS}, the value of git config + * {@code core.commentChar} will be used. + * + * @param commentChar + * the comment character, or {@code null} to use the value from + * the git config + * @return {@code this} + * @since 6.1 + */ + public CommitCommand setCommentCharacter(Character commentChar) { + checkCallable(); + this.commentChar = commentChar; + return this; + } + + /** * Set whether to allow to create an empty commit * * @param allowEmpty @@ -806,7 +879,7 @@ public class CommitCommand extends GitCommand<RevCommit> { * command line. * * @param amend - * whether to ammend the tip of the current branch + * whether to amend the tip of the current branch * @return {@code this} */ public CommitCommand setAmend(boolean amend) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java index 1e524fadab..805a886392 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java @@ -11,6 +11,7 @@ package org.eclipse.jgit.api; import static org.eclipse.jgit.lib.Constants.R_REFS; import static org.eclipse.jgit.lib.Constants.R_TAGS; +import static org.eclipse.jgit.lib.TypedConfigGetter.UNSET_INT; import java.io.IOException; import java.text.MessageFormat; @@ -33,6 +34,7 @@ import org.eclipse.jgit.errors.InvalidPatternException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.fnmatch.FileNameMatcher; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.AbbrevConfig; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; @@ -89,6 +91,11 @@ public class DescribeCommand extends GitCommand<String> { private boolean always; /** + * The prefix length to use when abbreviating a commit hash. + */ + private int abbrev = UNSET_INT; + + /** * Constructor for DescribeCommand. * * @param repo @@ -205,12 +212,33 @@ public class DescribeCommand extends GitCommand<String> { return this; } + /** + * Sets the prefix length to use when abbreviating an object SHA-1. + * + * @param abbrev + * minimum length of the abbreviated string. Must be in the range + * [{@value AbbrevConfig#MIN_ABBREV}, + * {@value Constants#OBJECT_ID_STRING_LENGTH}]. + * @return {@code this} + * @since 6.1 + */ + public DescribeCommand setAbbrev(int abbrev) { + if (abbrev == 0) { + this.abbrev = 0; + } else { + this.abbrev = AbbrevConfig.capAbbrev(abbrev); + } + return this; + } + private String longDescription(Ref tag, int depth, ObjectId tip) throws IOException { - return String.format( - "%s-%d-g%s", formatRefName(tag.getName()), //$NON-NLS-1$ - Integer.valueOf(depth), w.getObjectReader().abbreviate(tip) - .name()); + if (abbrev == 0) { + return formatRefName(tag.getName()); + } + return String.format("%s-%d-g%s", formatRefName(tag.getName()), //$NON-NLS-1$ + Integer.valueOf(depth), + w.getObjectReader().abbreviate(tip, abbrev).name()); } /** @@ -302,6 +330,9 @@ public class DescribeCommand extends GitCommand<String> { if (target == null) { setTarget(Constants.HEAD); } + if (abbrev == UNSET_INT) { + abbrev = AbbrevConfig.parseFromConfig(repo).get(); + } Collection<Ref> tagList = repo.getRefDatabase() .getRefsByPrefix(useAll ? R_REFS : R_TAGS); @@ -413,7 +444,12 @@ public class DescribeCommand extends GitCommand<String> { // if all the nodes are dominated by all the tags, the walk stops if (candidates.isEmpty()) { - return always ? w.getObjectReader().abbreviate(target).name() : null; + return always + ? w.getObjectReader() + .abbreviate(target, + AbbrevConfig.capAbbrev(abbrev)) + .name() + : null; } Candidate best = Collections.min(candidates, 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 ef56d802c8..ce068b6306 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java @@ -405,7 +405,7 @@ public class MergeCommand extends GitCommand<MergeResult> { failingPaths, null); } String mergeMessageWithConflicts = new MergeMessageFormatter() - .formatWithConflicts(mergeMessage, unmergedPaths); + .formatWithConflicts(mergeMessage, unmergedPaths, '#'); repo.writeMergeCommitMsg(mergeMessageWithConflicts); return new MergeResult(null, merger.getBaseCommitId(), new ObjectId[] { headCommit.getId(), diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java index aa5a63499c..08353dfdfa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others + * Copyright (C) 2010, 2022 Chris Aniszczyk <caniszczyk@gmail.com> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -21,7 +21,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.eclipse.jgit.api.errors.DetachedHeadException; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.InvalidRefNameException; import org.eclipse.jgit.api.errors.InvalidRemoteException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.errors.NotSupportedException; @@ -29,11 +31,16 @@ import org.eclipse.jgit.errors.TooLargeObjectInPackException; import org.eclipse.jgit.errors.TooLargePackException; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.BranchConfig; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.PushConfig; +import org.eclipse.jgit.transport.PushConfig.PushDefault; import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.RefLeaseSpec; import org.eclipse.jgit.transport.RefSpec; @@ -52,7 +59,7 @@ import org.eclipse.jgit.transport.Transport; public class PushCommand extends TransportCommand<PushCommand, Iterable<PushResult>> { - private String remote = Constants.DEFAULT_REMOTE_NAME; + private String remote; private final List<RefSpec> refSpecs; @@ -71,6 +78,10 @@ public class PushCommand extends private List<String> pushOptions; + // Legacy behavior as default. Use setPushDefault(null) to determine the + // value from the git config. + private PushDefault pushDefault = PushDefault.CURRENT; + /** * <p> * Constructor for PushCommand. @@ -98,19 +109,20 @@ public class PushCommand extends InvalidRemoteException, org.eclipse.jgit.api.errors.TransportException { checkCallable(); + setCallable(false); ArrayList<PushResult> pushResults = new ArrayList<>(3); try { + Config config = repo.getConfig(); + remote = determineRemote(config, remote); if (refSpecs.isEmpty()) { - RemoteConfig config = new RemoteConfig(repo.getConfig(), + RemoteConfig rc = new RemoteConfig(config, getRemote()); - refSpecs.addAll(config.getPushRefSpecs()); - } - if (refSpecs.isEmpty()) { - Ref head = repo.exactRef(Constants.HEAD); - if (head != null && head.isSymbolic()) - refSpecs.add(new RefSpec(head.getLeaf().getName())); + refSpecs.addAll(rc.getPushRefSpecs()); + if (refSpecs.isEmpty()) { + determineDefaultRefSpecs(config); + } } if (force) { @@ -118,8 +130,8 @@ public class PushCommand extends refSpecs.set(i, refSpecs.get(i).setForceUpdate(true)); } - final List<Transport> transports; - transports = Transport.openAll(repo, remote, Transport.Operation.PUSH); + List<Transport> transports = Transport.openAll(repo, remote, + Transport.Operation.PUSH); for (@SuppressWarnings("resource") // Explicitly closed in finally final Transport transport : transports) { transport.setPushThin(thin); @@ -171,6 +183,102 @@ public class PushCommand extends return pushResults; } + private String determineRemote(Config config, String remoteName) + throws IOException { + if (remoteName != null) { + return remoteName; + } + Ref head = repo.exactRef(Constants.HEAD); + String effectiveRemote = null; + BranchConfig branchCfg = null; + if (head != null && head.isSymbolic()) { + String currentBranch = head.getLeaf().getName(); + branchCfg = new BranchConfig(config, + Repository.shortenRefName(currentBranch)); + effectiveRemote = branchCfg.getPushRemote(); + } + if (effectiveRemote == null) { + effectiveRemote = config.getString( + ConfigConstants.CONFIG_REMOTE_SECTION, null, + ConfigConstants.CONFIG_KEY_PUSH_DEFAULT); + if (effectiveRemote == null && branchCfg != null) { + effectiveRemote = branchCfg.getRemote(); + } + } + if (effectiveRemote == null) { + effectiveRemote = Constants.DEFAULT_REMOTE_NAME; + } + return effectiveRemote; + } + + private String getCurrentBranch() + throws IOException, DetachedHeadException { + Ref head = repo.exactRef(Constants.HEAD); + if (head != null && head.isSymbolic()) { + return head.getLeaf().getName(); + } + throw new DetachedHeadException(); + } + + private void determineDefaultRefSpecs(Config config) + throws IOException, GitAPIException { + if (pushDefault == null) { + pushDefault = config.get(PushConfig::new).getPushDefault(); + } + switch (pushDefault) { + case CURRENT: + refSpecs.add(new RefSpec(getCurrentBranch())); + break; + case MATCHING: + refSpecs.add(new RefSpec(":")); //$NON-NLS-1$ + break; + case NOTHING: + throw new InvalidRefNameException( + JGitText.get().pushDefaultNothing); + case SIMPLE: + case UPSTREAM: + String currentBranch = getCurrentBranch(); + BranchConfig branchCfg = new BranchConfig(config, + Repository.shortenRefName(currentBranch)); + String fetchRemote = branchCfg.getRemote(); + if (fetchRemote == null) { + fetchRemote = Constants.DEFAULT_REMOTE_NAME; + } + boolean isTriangular = !fetchRemote.equals(remote); + if (isTriangular) { + if (PushDefault.UPSTREAM.equals(pushDefault)) { + throw new InvalidRefNameException(MessageFormat.format( + JGitText.get().pushDefaultTriangularUpstream, + remote, fetchRemote)); + } + // Strange, but consistent with C git: "simple" doesn't even + // check whether there is a configured upstream, and if so, that + // it is equal to the local branch name. It just becomes + // "current". + refSpecs.add(new RefSpec(currentBranch)); + } else { + String trackedBranch = branchCfg.getMerge(); + if (branchCfg.isRemoteLocal() || trackedBranch == null + || !trackedBranch.startsWith(Constants.R_HEADS)) { + throw new InvalidRefNameException(MessageFormat.format( + JGitText.get().pushDefaultNoUpstream, + currentBranch)); + } + if (PushDefault.SIMPLE.equals(pushDefault) + && !trackedBranch.equals(currentBranch)) { + throw new InvalidRefNameException(MessageFormat.format( + JGitText.get().pushDefaultSimple, currentBranch, + trackedBranch)); + } + refSpecs.add(new RefSpec(currentBranch + ':' + trackedBranch)); + } + break; + default: + throw new InvalidRefNameException(MessageFormat + .format(JGitText.get().pushDefaultUnknown, pushDefault)); + } + } + /** * The remote (uri or name) used for the push operation. If no remote is * set, the default value of <code>Constants.DEFAULT_REMOTE_NAME</code> will @@ -336,9 +444,37 @@ public class PushCommand extends } /** + * Retrieves the {@link PushDefault} currently set. + * + * @return the {@link PushDefault}, or {@code null} if not set + * @since 6.1 + */ + public PushDefault getPushDefault() { + return pushDefault; + } + + /** + * Sets an explicit {@link PushDefault}. The default used if this is not + * called is {@link PushDefault#CURRENT} for compatibility reasons with + * earlier JGit versions. + * + * @param pushDefault + * {@link PushDefault} to set; if {@code null} the value defined + * in the git config will be used. + * + * @return {@code this} + * @since 6.1 + */ + public PushCommand setPushDefault(PushDefault pushDefault) { + checkCallable(); + this.pushDefault = pushDefault; + return this; + } + + /** * Push all branches under refs/heads/*. * - * @return {code this} + * @return {@code this} */ public PushCommand setPushAll() { refSpecs.add(Transport.REFSPEC_PUSH_ALL); @@ -348,7 +484,7 @@ public class PushCommand extends /** * Push all tags under refs/tags/*. * - * @return {code this} + * @return {@code this} */ public PushCommand setPushTags() { refSpecs.add(Transport.REFSPEC_TAGS); 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 a26ffc2e66..2b0d8ce1c9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.api.RebaseResult.Status; import org.eclipse.jgit.api.ResetCommand.ResetType; import org.eclipse.jgit.api.errors.CheckoutConflictException; @@ -52,6 +53,8 @@ import org.eclipse.jgit.errors.RevisionSyntaxException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AbbreviatedObjectId; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.CommitConfig; +import org.eclipse.jgit.lib.CommitConfig.CleanupMode; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; @@ -205,6 +208,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> { private InteractiveHandler interactiveHandler; + private CommitConfig commitConfig; + private boolean stopAfterInitialization = false; private RevCommit newHead; @@ -246,6 +251,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { lastStepWasForward = false; checkCallable(); checkParameters(); + commitConfig = repo.getConfig().get(CommitConfig.KEY); try { switch (operation) { case ABORT: @@ -441,11 +447,16 @@ public class RebaseCommand extends GitCommand<RebaseResult> { return null; // continue rebase process on pick command case REWORD: String oldMessage = commitToPick.getFullMessage(); - String newMessage = interactiveHandler - .modifyCommitMessage(oldMessage); + CleanupMode mode = commitConfig.resolve(CleanupMode.DEFAULT, true); + boolean[] doChangeId = { false }; + String newMessage = editCommitMessage(doChangeId, oldMessage, mode); try (Git git = new Git(repo)) { - newHead = git.commit().setMessage(newMessage).setAmend(true) - .setNoVerify(true).call(); + newHead = git.commit() + .setMessage(newMessage) + .setAmend(true) + .setNoVerify(true) + .setInsertChangeId(doChangeId[0]) + .call(); } return null; case EDIT: @@ -460,17 +471,49 @@ public class RebaseCommand extends GitCommand<RebaseResult> { resetSoftToParent(); List<RebaseTodoLine> steps = repo.readRebaseTodo( rebaseState.getPath(GIT_REBASE_TODO), false); - RebaseTodoLine nextStep = steps.isEmpty() ? null : steps.get(0); + boolean isLast = steps.isEmpty(); + if (!isLast) { + switch (steps.get(0).getAction()) { + case FIXUP: + case SQUASH: + break; + default: + isLast = true; + break; + } + } File messageFixupFile = rebaseState.getFile(MESSAGE_FIXUP); File messageSquashFile = rebaseState.getFile(MESSAGE_SQUASH); - if (isSquash && messageFixupFile.exists()) + if (isSquash && messageFixupFile.exists()) { messageFixupFile.delete(); - newHead = doSquashFixup(isSquash, commitToPick, nextStep, + } + newHead = doSquashFixup(isSquash, commitToPick, isLast, messageFixupFile, messageSquashFile); } return null; } + private String editCommitMessage(boolean[] doChangeId, String message, + @NonNull CleanupMode mode) { + String newMessage; + CommitConfig.CleanupMode cleanup; + if (interactiveHandler instanceof InteractiveHandler2) { + InteractiveHandler2.ModifyResult modification = ((InteractiveHandler2) interactiveHandler) + .editCommitMessage(message, mode, '#'); + newMessage = modification.getMessage(); + cleanup = modification.getCleanupMode(); + if (CleanupMode.DEFAULT.equals(cleanup)) { + cleanup = mode; + } + doChangeId[0] = modification.shouldAddChangeId(); + } else { + newMessage = interactiveHandler.modifyCommitMessage(message); + cleanup = CommitConfig.CleanupMode.STRIP; + doChangeId[0] = false; + } + return CommitConfig.cleanText(newMessage, cleanup, '#'); + } + private RebaseResult cherryPickCommit(RevCommit commitToPick) throws IOException, GitAPIException, NoMessageException, UnmergedPathsException, ConcurrentRefUpdateException, @@ -707,7 +750,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { } private RevCommit doSquashFixup(boolean isSquash, RevCommit commitToPick, - RebaseTodoLine nextStep, File messageFixup, File messageSquash) + boolean isLast, File messageFixup, File messageSquash) throws IOException, GitAPIException { if (!messageSquash.exists()) { @@ -717,24 +760,20 @@ public class RebaseCommand extends GitCommand<RebaseResult> { initializeSquashFixupFile(MESSAGE_SQUASH, previousCommit.getFullMessage()); - if (!isSquash) - initializeSquashFixupFile(MESSAGE_FIXUP, - previousCommit.getFullMessage()); + if (!isSquash) { + rebaseState.createFile(MESSAGE_FIXUP, + previousCommit.getFullMessage()); + } } - String currSquashMessage = rebaseState - .readFile(MESSAGE_SQUASH); + String currSquashMessage = rebaseState.readFile(MESSAGE_SQUASH); int count = parseSquashFixupSequenceCount(currSquashMessage) + 1; String content = composeSquashMessage(isSquash, commitToPick, currSquashMessage, count); rebaseState.createFile(MESSAGE_SQUASH, content); - if (messageFixup.exists()) - rebaseState.createFile(MESSAGE_FIXUP, content); - return squashIntoPrevious( - !messageFixup.exists(), - nextStep); + return squashIntoPrevious(!messageFixup.exists(), isLast); } private void resetSoftToParent() throws IOException, @@ -756,26 +795,30 @@ public class RebaseCommand extends GitCommand<RebaseResult> { } private RevCommit squashIntoPrevious(boolean sequenceContainsSquash, - RebaseTodoLine nextStep) + boolean isLast) throws IOException, GitAPIException { RevCommit retNewHead; - String commitMessage = rebaseState - .readFile(MESSAGE_SQUASH); - + String commitMessage; + if (!isLast || sequenceContainsSquash) { + commitMessage = rebaseState.readFile(MESSAGE_SQUASH); + } else { + commitMessage = rebaseState.readFile(MESSAGE_FIXUP); + } try (Git git = new Git(repo)) { - if (nextStep == null || ((nextStep.getAction() != Action.FIXUP) - && (nextStep.getAction() != Action.SQUASH))) { - // this is the last step in this sequence + if (isLast) { + boolean[] doChangeId = { false }; if (sequenceContainsSquash) { - commitMessage = interactiveHandler - .modifyCommitMessage(commitMessage); + commitMessage = editCommitMessage(doChangeId, commitMessage, + CleanupMode.STRIP); } retNewHead = git.commit() - .setMessage(stripCommentLines(commitMessage)) - .setAmend(true).setNoVerify(true).call(); + .setMessage(commitMessage) + .setAmend(true) + .setNoVerify(true) + .setInsertChangeId(doChangeId[0]) + .call(); rebaseState.getFile(MESSAGE_SQUASH).delete(); rebaseState.getFile(MESSAGE_FIXUP).delete(); - } else { // Next step is either Squash or Fixup retNewHead = git.commit().setMessage(commitMessage) @@ -785,21 +828,6 @@ public class RebaseCommand extends GitCommand<RebaseResult> { return retNewHead; } - private static String stripCommentLines(String commitMessage) { - StringBuilder result = new StringBuilder(); - for (String line : commitMessage.split("\n")) { //$NON-NLS-1$ - if (!line.trim().startsWith("#")) //$NON-NLS-1$ - result.append(line).append("\n"); //$NON-NLS-1$ - } - if (!commitMessage.endsWith("\n")) { //$NON-NLS-1$ - int bufferSize = result.length(); - if (bufferSize > 0 && result.charAt(bufferSize - 1) == '\n') { - result.deleteCharAt(bufferSize - 1); - } - } - return result.toString(); - } - @SuppressWarnings("nls") private static String composeSquashMessage(boolean isSquash, RevCommit commitToPick, String currSquashMessage, int count) { @@ -1625,26 +1653,106 @@ public class RebaseCommand extends GitCommand<RebaseResult> { } /** - * Allows configure rebase interactive process and modify commit message + * Allows to configure the interactive rebase process steps and to modify + * commit messages. */ public interface InteractiveHandler { + /** - * Given list of {@code steps} should be modified according to user - * rebase configuration + * Callback API to modify the initial list of interactive rebase steps. + * * @param steps - * initial configuration of rebase interactive + * initial configuration of interactive rebase */ void prepareSteps(List<RebaseTodoLine> steps); /** - * Used for editing commit message on REWORD + * Used for editing commit message on REWORD or SQUASH. * - * @param commit + * @param message + * existing commit message * @return new commit message */ - String modifyCommitMessage(String commit); + String modifyCommitMessage(String message); } + /** + * Extends {@link InteractiveHandler} with an enhanced callback for editing + * commit messages. + * + * @since 6.1 + */ + public interface InteractiveHandler2 extends InteractiveHandler { + + /** + * Callback API for editing a commit message on REWORD or SQUASH. + * <p> + * The callback gets the comment character currently set, and the + * clean-up mode. It can use this information when presenting the + * message to the user, and it also has the possibility to clean the + * message itself (in which case the returned {@link ModifyResult} + * should have {@link CleanupMode#VERBATIM} set lest JGit cleans the + * message again). It can also override the initial clean-up mode by + * returning clean-up mode other than {@link CleanupMode#DEFAULT}. If it + * does return {@code DEFAULT}, the passed-in {@code mode} will be + * applied. + * </p> + * + * @param message + * existing commit message + * @param mode + * {@link CleanupMode} currently set + * @param commentChar + * comment character used + * @return a {@link ModifyResult} + */ + @NonNull + ModifyResult editCommitMessage(@NonNull String message, + @NonNull CleanupMode mode, char commentChar); + + @Override + default String modifyCommitMessage(String message) { + // Should actually not be called; but do something reasonable anyway + ModifyResult result = editCommitMessage( + message == null ? "" : message, CleanupMode.STRIP, //$NON-NLS-1$ + '#'); + return result.getMessage(); + } + + /** + * Describes the result of editing a commit message: the new message, + * and how it should be cleaned. + */ + interface ModifyResult { + + /** + * Retrieves the new commit message. + * + * @return the message + */ + @NonNull + String getMessage(); + + /** + * Tells how the message returned by {@link #getMessage()} should be + * cleaned. + * + * @return the {@link CleanupMode} + */ + @NonNull + CleanupMode getCleanupMode(); + + /** + * Tells whether a Gerrit Change-Id should be computed and added to + * the commit message, as with + * {@link CommitCommand#setInsertChangeId(boolean)}. + * + * @return {@code true} if a Change-Id should be handled, + * {@code false} otherwise + */ + boolean shouldAddChangeId(); + } + } PersonIdent parseAuthor(byte[] raw) { if (raw.length == 0) 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 22ef4d0a32..db88ad8dc9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java @@ -9,6 +9,8 @@ */ package org.eclipse.jgit.api; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH; + import java.io.IOException; import java.text.MessageFormat; import java.util.LinkedList; @@ -128,8 +130,9 @@ public class RevertCommand extends GitCommand<RevCommit> { revWalk.parseHeaders(srcParent); String ourName = calculateOurName(headRef); - String revertName = srcCommit.getId().abbreviate(7).name() - + " " + srcCommit.getShortMessage(); //$NON-NLS-1$ + String revertName = srcCommit.getId() + .abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH).name() + " " //$NON-NLS-1$ + + srcCommit.getShortMessage(); ResolveMerger merger = (ResolveMerger) strategy.newMerger(repo); merger.setWorkingTreeIterator(new FileTreeIterator(repo)); @@ -183,8 +186,8 @@ public class RevertCommand extends GitCommand<RevCommit> { merger.getMergeResults(), failingPaths, null); if (!merger.failed() && !unmergedPaths.isEmpty()) { String message = new MergeMessageFormatter() - .formatWithConflicts(newMessage, - merger.getUnmergedPaths()); + .formatWithConflicts(newMessage, + merger.getUnmergedPaths(), '#'); repo.writeRevertHead(srcCommit.getId()); repo.writeMergeCommitMsg(message); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java index 35fd8992b6..f7a1f4eff8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java @@ -9,6 +9,8 @@ */ package org.eclipse.jgit.api; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -302,7 +304,8 @@ public class StashCreateCommand extends GitCommand<RevCommit> { builder.setParentId(headCommit); builder.setTreeId(cache.writeTree(inserter)); builder.setMessage(MessageFormat.format(indexMessage, branch, - headCommit.abbreviate(7).name(), + headCommit.abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH) + .name(), headCommit.getShortMessage())); ObjectId indexCommit = inserter.insert(builder); @@ -319,7 +322,10 @@ public class StashCreateCommand extends GitCommand<RevCommit> { builder.setParentIds(new ObjectId[0]); builder.setTreeId(untrackedDirCache.writeTree(inserter)); builder.setMessage(MessageFormat.format(MSG_UNTRACKED, - branch, headCommit.abbreviate(7).name(), + branch, + headCommit + .abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH) + .name(), headCommit.getShortMessage())); untrackedCommit = inserter.insert(builder); } @@ -339,7 +345,8 @@ public class StashCreateCommand extends GitCommand<RevCommit> { builder.addParentId(untrackedCommit); builder.setMessage(MessageFormat.format( workingDirectoryMessage, branch, - headCommit.abbreviate(7).name(), + headCommit.abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH) + .name(), headCommit.getShortMessage())); builder.setTreeId(cache.writeTree(inserter)); commitId = inserter.insert(builder); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesHandler.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesHandler.java index 638dd827ed..7ec78597fa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesHandler.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesHandler.java @@ -1,43 +1,11 @@ /* - * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> + * Copyright (C) 2015, 2022 Ivan Motsch <ivan.motsch@bsiag.com> and others * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php + * 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. * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.attributes; @@ -46,6 +14,7 @@ import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.function.Supplier; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.attributes.Attribute.State; @@ -84,6 +53,8 @@ public class AttributesHandler { private final TreeWalk treeWalk; + private final Supplier<CanonicalTreeParser> attributesTree; + private final AttributesNode globalNode; private final AttributesNode infoNode; @@ -98,22 +69,41 @@ public class AttributesHandler { * @param treeWalk * a {@link org.eclipse.jgit.treewalk.TreeWalk} * @throws java.io.IOException + * @deprecated since 6.1, use {@link #AttributesHandler(TreeWalk, Supplier)} + * instead */ + @Deprecated public AttributesHandler(TreeWalk treeWalk) throws IOException { + this(treeWalk, () -> treeWalk.getTree(CanonicalTreeParser.class)); + } + + /** + * Create an {@link org.eclipse.jgit.attributes.AttributesHandler} with + * default rules as well as merged rules from global, info and worktree root + * attributes + * + * @param treeWalk + * a {@link org.eclipse.jgit.treewalk.TreeWalk} + * @param attributesTree + * the tree to read .gitattributes from + * @throws java.io.IOException + * @since 6.1 + */ + public AttributesHandler(TreeWalk treeWalk, + Supplier<CanonicalTreeParser> attributesTree) throws IOException { this.treeWalk = treeWalk; - AttributesNodeProvider attributesNodeProvider =treeWalk.getAttributesNodeProvider(); + this.attributesTree = attributesTree; + AttributesNodeProvider attributesNodeProvider = treeWalk + .getAttributesNodeProvider(); this.globalNode = attributesNodeProvider != null ? attributesNodeProvider.getGlobalAttributesNode() : null; this.infoNode = attributesNodeProvider != null ? attributesNodeProvider.getInfoAttributesNode() : null; AttributesNode rootNode = attributesNode(treeWalk, - rootOf( - treeWalk.getTree(WorkingTreeIterator.class)), - rootOf( - treeWalk.getTree(DirCacheIterator.class)), - rootOf(treeWalk - .getTree(CanonicalTreeParser.class))); + rootOf(treeWalk.getTree(WorkingTreeIterator.class)), + rootOf(treeWalk.getTree(DirCacheIterator.class)), + rootOf(attributesTree.get())); expansions.put(BINARY_RULE_KEY, BINARY_RULE_ATTRIBUTES); for (AttributesNode node : new AttributesNode[] { globalNode, rootNode, @@ -152,7 +142,7 @@ public class AttributesHandler { isDirectory, treeWalk.getTree(WorkingTreeIterator.class), treeWalk.getTree(DirCacheIterator.class), - treeWalk.getTree(CanonicalTreeParser.class), + attributesTree.get(), attributes); // Gets the attributes located in the global attribute file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java index 49da95c9ab..1a5f74f98a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java @@ -18,6 +18,7 @@ import static org.eclipse.jgit.diff.DiffEntry.ChangeType.MODIFY; import static org.eclipse.jgit.diff.DiffEntry.ChangeType.RENAME; import static org.eclipse.jgit.diff.DiffEntry.Side.NEW; import static org.eclipse.jgit.diff.DiffEntry.Side.OLD; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH; import static org.eclipse.jgit.lib.Constants.encode; import static org.eclipse.jgit.lib.Constants.encodeASCII; import static org.eclipse.jgit.lib.FileMode.GITLINK; @@ -90,7 +91,7 @@ public class DiffFormatter implements AutoCloseable { private int context = 3; - private int abbreviationLength = 7; + private int abbreviationLength = OBJECT_ID_ABBREV_STRING_LENGTH; private DiffAlgorithm diffAlgorithm; 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 c904a782db..3d50a82155 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -4,7 +4,8 @@ * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br> * Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org> * Copyright (C) 2010, Chrisian Halstrick <christian.halstrick@sap.com> - * Copyright (C) 2019-2020, Andre Bossert <andre.bossert@siemens.com> + * Copyright (C) 2019, 2020, Andre Bossert <andre.bossert@siemens.com> + * Copyright (C) 2017, 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 @@ -299,7 +300,7 @@ public class DirCacheCheckout { walk = new NameConflictTreeWalk(repo); builder = dc.builder(); - addTree(walk, headCommitTree); + walk.setHead(addTree(walk, headCommitTree)); addTree(walk, mergeCommitTree); int dciPos = walk.addTree(new DirCacheBuildIterator(builder)); walk.addTree(workingTree); @@ -315,13 +316,6 @@ public class DirCacheCheckout { } } - private void addTree(TreeWalk tw, ObjectId id) throws MissingObjectException, IncorrectObjectTypeException, IOException { - if (id == null) - tw.addTree(new EmptyTreeIterator()); - else - tw.addTree(id); - } - /** * Scan index and merge tree (no HEAD). Used e.g. for initial checkout when * there is no head yet. @@ -341,7 +335,7 @@ public class DirCacheCheckout { builder = dc.builder(); walk = new NameConflictTreeWalk(repo); - addTree(walk, mergeCommitTree); + walk.setHead(addTree(walk, mergeCommitTree)); int dciPos = walk.addTree(new DirCacheBuildIterator(builder)); walk.addTree(workingTree); workingTree.setDirCacheIterator(walk, dciPos); @@ -356,6 +350,14 @@ public class DirCacheCheckout { conflicts.removeAll(removed); } + private int addTree(TreeWalk tw, ObjectId id) throws MissingObjectException, + IncorrectObjectTypeException, IOException { + if (id == null) { + return tw.addTree(new EmptyTreeIterator()); + } + return tw.addTree(id); + } + /** * Processing an entry in the context of {@link #prescanOneTree()} when only * one tree is given @@ -382,17 +384,14 @@ public class DirCacheCheckout { // failOnConflict is false. Putting something to conflicts // would mean we delete it. Instead we want the mergeCommit // content to be checked out. - update(m.getEntryPathString(), m.getEntryObjectId(), - m.getEntryFileMode()); + update(m); } } else - update(m.getEntryPathString(), m.getEntryObjectId(), - m.getEntryFileMode()); + update(m); } else if (f == null || !m.idEqual(i)) { // The working tree file is missing or the merge content differs // from index content - update(m.getEntryPathString(), m.getEntryObjectId(), - m.getEntryFileMode()); + update(m); } else if (i.getDirCacheEntry() != null) { // The index contains a file (and not a folder) if (f.isModified(i.getDirCacheEntry(), true, @@ -400,8 +399,7 @@ public class DirCacheCheckout { || i.getDirCacheEntry().getStage() != 0) // The working tree file is dirty or the index contains a // conflict - update(m.getEntryPathString(), m.getEntryObjectId(), - m.getEntryFileMode()); + update(m); else { // update the timestamp of the index with the one from the // file if not set, as we are sure to be in sync here. @@ -802,7 +800,7 @@ public class DirCacheCheckout { if (f != null && isModifiedSubtree_IndexWorkingtree(name)) { conflict(name, dce, h, m); // 1 } else { - update(name, mId, mMode); // 2 + update(1, name, mId, mMode); // 2 } break; @@ -828,7 +826,7 @@ public class DirCacheCheckout { // are found later break; case 0xD0F: // 19 - update(name, mId, mMode); + update(1, name, mId, mMode); break; case 0xDF0: // conflict without a rule case 0x0FD: // 15 @@ -839,7 +837,7 @@ public class DirCacheCheckout { if (isModifiedSubtree_IndexWorkingtree(name)) conflict(name, dce, h, m); // 8 else - update(name, mId, mMode); // 7 + update(1, name, mId, mMode); // 7 } else conflict(name, dce, h, m); // 9 break; @@ -859,7 +857,7 @@ public class DirCacheCheckout { break; case 0x0DF: // 16 17 if (!isModifiedSubtree_IndexWorkingtree(name)) - update(name, mId, mMode); + update(1, name, mId, mMode); else conflict(name, dce, h, m); break; @@ -929,7 +927,7 @@ public class DirCacheCheckout { // At least one of Head, Index, Merge is not empty // -> only Merge contains something for this path. Use it! // Potentially update the file - update(name, mId, mMode); // 1 + update(1, name, mId, mMode); // 1 else if (m == null) // Nothing in Merge // Something in Head @@ -947,7 +945,7 @@ public class DirCacheCheckout { // find in Merge. Potentially updates the file. if (equalIdAndMode(hId, hMode, mId, mMode)) { if (initialCheckout || force) { - update(name, mId, mMode); + update(1, name, mId, mMode); } else { keep(name, dce, f); } @@ -1131,7 +1129,7 @@ public class DirCacheCheckout { // TODO check that we don't overwrite some unsaved // file content - update(name, mId, mMode); + update(1, name, mId, mMode); } else if (dce != null && (f != null && f.isModified(dce, true, this.walk.getObjectReader()))) { @@ -1150,7 +1148,7 @@ public class DirCacheCheckout { // -> Standard case when switching between branches: // Nothing new in index but something different in // Merge. Update index and file - update(name, mId, mMode); + update(1, name, mId, mMode); } } else { // Head differs from index or merge is same as index @@ -1237,12 +1235,17 @@ public class DirCacheCheckout { removed.add(path); } - private void update(String path, ObjectId mId, FileMode mode) - throws IOException { + private void update(CanonicalTreeParser tree) throws IOException { + update(0, tree.getEntryPathString(), tree.getEntryObjectId(), + tree.getEntryFileMode()); + } + + private void update(int index, String path, ObjectId mId, + FileMode mode) throws IOException { if (!FileMode.TREE.equals(mode)) { updated.put(path, new CheckoutMetadata( - walk.getEolStreamType(CHECKOUT_OP), - walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE))); + walk.getCheckoutEolStreamType(index), + walk.getSmudgeCommand(index))); DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0); entry.setObjectId(mId); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java new file mode 100644 index 0000000000..e6626aece3 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java @@ -0,0 +1,319 @@ +/* + * Copyright (C) 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 + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.gitrepo; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.eclipse.jgit.lib.Constants.R_TAGS; + +import java.io.IOException; +import java.net.URI; +import java.text.MessageFormat; +import java.util.List; + +import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheBuilder; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.gitrepo.RepoCommand.ManifestErrorException; +import org.eclipse.jgit.gitrepo.RepoCommand.RemoteFile; +import org.eclipse.jgit.gitrepo.RepoCommand.RemoteReader; +import org.eclipse.jgit.gitrepo.RepoCommand.RemoteUnavailableException; +import org.eclipse.jgit.gitrepo.RepoProject.CopyFile; +import org.eclipse.jgit.gitrepo.RepoProject.LinkFile; +import org.eclipse.jgit.gitrepo.internal.RepoText; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.CommitBuilder; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.RefUpdate.Result; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.util.FileUtils; + +/** + * Writes .gitmodules and gitlinks of parsed manifest projects into a bare + * repository. + * + * To write on a regular repository, see {@link RegularSuperprojectWriter}. + */ +class BareSuperprojectWriter { + private static final int LOCK_FAILURE_MAX_RETRIES = 5; + + // Retry exponentially with delays in this range + private static final int LOCK_FAILURE_MIN_RETRY_DELAY_MILLIS = 50; + + private static final int LOCK_FAILURE_MAX_RETRY_DELAY_MILLIS = 5000; + + private final Repository repo; + + private final URI targetUri; + + private final String targetBranch; + + private final RemoteReader callback; + + private final BareWriterConfig config; + + private final PersonIdent author; + + private List<ExtraContent> extraContents; + + static class BareWriterConfig { + boolean ignoreRemoteFailures = false; + + boolean recordRemoteBranch = true; + + boolean recordSubmoduleLabels = true; + + boolean recordShallowSubmodules = true; + + static BareWriterConfig getDefault() { + return new BareWriterConfig(); + } + + private BareWriterConfig() { + } + } + + static class ExtraContent { + final String path; + + final String content; + + ExtraContent(String path, String content) { + this.path = path; + this.content = content; + } + } + + BareSuperprojectWriter(Repository repo, URI targetUri, + String targetBranch, + PersonIdent author, RemoteReader callback, + BareWriterConfig config, + List<ExtraContent> extraContents) { + assert (repo.isBare()); + this.repo = repo; + this.targetUri = targetUri; + this.targetBranch = targetBranch; + this.author = author; + this.callback = callback; + this.config = config; + this.extraContents = extraContents; + } + + RevCommit write(List<RepoProject> repoProjects) + throws GitAPIException { + DirCache index = DirCache.newInCore(); + ObjectInserter inserter = repo.newObjectInserter(); + + try (RevWalk rw = new RevWalk(repo)) { + prepareIndex(repoProjects, index, inserter); + ObjectId treeId = index.writeTree(inserter); + long prevDelay = 0; + for (int i = 0; i < LOCK_FAILURE_MAX_RETRIES - 1; i++) { + try { + return commitTreeOnCurrentTip(inserter, rw, treeId); + } catch (ConcurrentRefUpdateException e) { + prevDelay = FileUtils.delay(prevDelay, + LOCK_FAILURE_MIN_RETRY_DELAY_MILLIS, + LOCK_FAILURE_MAX_RETRY_DELAY_MILLIS); + Thread.sleep(prevDelay); + repo.getRefDatabase().refresh(); + } + } + // In the last try, just propagate the exceptions + return commitTreeOnCurrentTip(inserter, rw, treeId); + } catch (IOException | InterruptedException e) { + throw new ManifestErrorException(e); + } + } + + private void prepareIndex(List<RepoProject> projects, DirCache index, + ObjectInserter inserter) throws IOException, GitAPIException { + Config cfg = new Config(); + StringBuilder attributes = new StringBuilder(); + DirCacheBuilder builder = index.builder(); + for (RepoProject proj : projects) { + String name = proj.getName(); + String path = proj.getPath(); + String url = proj.getUrl(); + ObjectId objectId; + if (ObjectId.isId(proj.getRevision())) { + objectId = ObjectId.fromString(proj.getRevision()); + } else { + objectId = callback.sha1(url, proj.getRevision()); + if (objectId == null && !config.ignoreRemoteFailures) { + throw new RemoteUnavailableException(url); + } + if (config.recordRemoteBranch) { + // "branch" field is only for non-tag references. + // Keep tags in "ref" field as hint for other tools. + String field = proj.getRevision().startsWith(R_TAGS) ? "ref" //$NON-NLS-1$ + : "branch"; //$NON-NLS-1$ + cfg.setString("submodule", name, field, //$NON-NLS-1$ + proj.getRevision()); + } + + if (config.recordShallowSubmodules + && proj.getRecommendShallow() != null) { + // The shallow recommendation is losing information. + // As the repo manifests stores the recommended + // depth in the 'clone-depth' field, while + // git core only uses a binary 'shallow = true/false' + // hint, we'll map any depth to 'shallow = true' + cfg.setBoolean("submodule", name, "shallow", //$NON-NLS-1$ //$NON-NLS-2$ + true); + } + } + if (config.recordSubmoduleLabels) { + StringBuilder rec = new StringBuilder(); + rec.append("/"); //$NON-NLS-1$ + rec.append(path); + for (String group : proj.getGroups()) { + rec.append(" "); //$NON-NLS-1$ + rec.append(group); + } + rec.append("\n"); //$NON-NLS-1$ + attributes.append(rec.toString()); + } + + URI submodUrl = URI.create(url); + if (targetUri != null) { + submodUrl = RepoCommand.relativize(targetUri, submodUrl); + } + cfg.setString("submodule", name, "path", path); //$NON-NLS-1$ //$NON-NLS-2$ + cfg.setString("submodule", name, "url", //$NON-NLS-1$ //$NON-NLS-2$ + submodUrl.toString()); + + // create gitlink + if (objectId != null) { + DirCacheEntry dcEntry = new DirCacheEntry(path); + dcEntry.setObjectId(objectId); + dcEntry.setFileMode(FileMode.GITLINK); + builder.add(dcEntry); + + for (CopyFile copyfile : proj.getCopyFiles()) { + RemoteFile rf = callback.readFileWithMode(url, + proj.getRevision(), copyfile.src); + objectId = inserter.insert(Constants.OBJ_BLOB, + rf.getContents()); + dcEntry = new DirCacheEntry(copyfile.dest); + dcEntry.setObjectId(objectId); + dcEntry.setFileMode(rf.getFileMode()); + builder.add(dcEntry); + } + for (LinkFile linkfile : proj.getLinkFiles()) { + String link; + if (linkfile.dest.contains("/")) { //$NON-NLS-1$ + link = FileUtils.relativizeGitPath( + linkfile.dest.substring(0, + linkfile.dest.lastIndexOf('/')), + proj.getPath() + "/" + linkfile.src); //$NON-NLS-1$ + } else { + link = proj.getPath() + "/" + linkfile.src; //$NON-NLS-1$ + } + + objectId = inserter.insert(Constants.OBJ_BLOB, + link.getBytes(UTF_8)); + dcEntry = new DirCacheEntry(linkfile.dest); + dcEntry.setObjectId(objectId); + dcEntry.setFileMode(FileMode.SYMLINK); + builder.add(dcEntry); + } + } + } + String content = cfg.toText(); + + // create a new DirCacheEntry for .gitmodules file. + DirCacheEntry dcEntry = new DirCacheEntry( + Constants.DOT_GIT_MODULES); + ObjectId objectId = inserter.insert(Constants.OBJ_BLOB, + content.getBytes(UTF_8)); + dcEntry.setObjectId(objectId); + dcEntry.setFileMode(FileMode.REGULAR_FILE); + builder.add(dcEntry); + + if (config.recordSubmoduleLabels) { + // create a new DirCacheEntry for .gitattributes file. + DirCacheEntry dcEntryAttr = new DirCacheEntry( + Constants.DOT_GIT_ATTRIBUTES); + ObjectId attrId = inserter.insert(Constants.OBJ_BLOB, + attributes.toString().getBytes(UTF_8)); + dcEntryAttr.setObjectId(attrId); + dcEntryAttr.setFileMode(FileMode.REGULAR_FILE); + builder.add(dcEntryAttr); + } + + for (ExtraContent ec : extraContents) { + DirCacheEntry extraDcEntry = new DirCacheEntry(ec.path); + + ObjectId oid = inserter.insert(Constants.OBJ_BLOB, + ec.content.getBytes(UTF_8)); + extraDcEntry.setObjectId(oid); + extraDcEntry.setFileMode(FileMode.REGULAR_FILE); + builder.add(extraDcEntry); + } + + builder.finish(); + } + + private RevCommit commitTreeOnCurrentTip(ObjectInserter inserter, + RevWalk rw, ObjectId treeId) + throws IOException, ConcurrentRefUpdateException { + ObjectId headId = repo.resolve(targetBranch + "^{commit}"); //$NON-NLS-1$ + if (headId != null + && rw.parseCommit(headId).getTree().getId().equals(treeId)) { + // No change. Do nothing. + return rw.parseCommit(headId); + } + + CommitBuilder commit = new CommitBuilder(); + commit.setTreeId(treeId); + if (headId != null) { + commit.setParentIds(headId); + } + commit.setAuthor(author); + commit.setCommitter(author); + commit.setMessage(RepoText.get().repoCommitMessage); + + ObjectId commitId = inserter.insert(commit); + inserter.flush(); + + RefUpdate ru = repo.updateRef(targetBranch); + ru.setNewObjectId(commitId); + ru.setExpectedOldObjectId(headId != null ? headId : ObjectId.zeroId()); + Result rc = ru.update(rw); + switch (rc) { + case NEW: + case FORCED: + case FAST_FORWARD: + // Successful. Do nothing. + break; + case REJECTED: + case LOCK_FAILURE: + throw new ConcurrentRefUpdateException(MessageFormat.format( + JGitText.get().cannotLock, targetBranch), ru.getRef(), rc); + default: + throw new JGitInternalException( + MessageFormat.format(JGitText.get().updatingRefFailed, + targetBranch, commitId.name(), rc)); + } + + return rw.parseCommit(commitId); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java index aa69a05112..726c7b430e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java @@ -139,7 +139,17 @@ public class ManifestParser extends DefaultHandler { xmlInRead++; final XMLReader xr; try { - xr = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setFeature( + "http://xml.org/sax/features/external-general-entities", //$NON-NLS-1$ + false); + spf.setFeature( + "http://xml.org/sax/features/external-parameter-entities", //$NON-NLS-1$ + false); + spf.setFeature( + "http://apache.org/xml/features/disallow-doctype-decl", //$NON-NLS-1$ + true); + xr = spf.newSAXParser().getXMLReader(); } catch (SAXException | ParserConfigurationException e) { throw new IOException(JGitText.get().noXMLParserAvailable, e); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RegularSuperprojectWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RegularSuperprojectWriter.java new file mode 100644 index 0000000000..afab9943a7 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RegularSuperprojectWriter.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 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 + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.gitrepo; + +import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME; +import static org.eclipse.jgit.lib.Constants.R_REMOTES; + +import java.io.IOException; +import java.util.List; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.SubmoduleAddCommand; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.gitrepo.RepoCommand.ManifestErrorException; +import org.eclipse.jgit.gitrepo.RepoProject.CopyFile; +import org.eclipse.jgit.gitrepo.RepoProject.LinkFile; +import org.eclipse.jgit.gitrepo.internal.RepoText; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ProgressMonitor; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; + +/** + * Writes .gitmodules and gitlinks of parsed manifest projects into a regular + * repository (using git submodule commands) + * + * To write on a bare repository, use {@link BareSuperprojectWriter} + */ +class RegularSuperprojectWriter { + + private Repository repo; + + private ProgressMonitor monitor; + + RegularSuperprojectWriter(Repository repo, ProgressMonitor monitor) { + this.repo = repo; + this.monitor = monitor; + } + + RevCommit write(List<RepoProject> repoProjects) + throws GitAPIException { + try (Git git = new Git(repo)) { + for (RepoProject proj : repoProjects) { + addSubmodule(proj.getName(), proj.getUrl(), proj.getPath(), + proj.getRevision(), proj.getCopyFiles(), + proj.getLinkFiles(), git); + } + return git.commit().setMessage(RepoText.get().repoCommitMessage) + .call(); + } catch (IOException e) { + throw new ManifestErrorException(e); + } + } + + private void addSubmodule(String name, String url, String path, + String revision, List<CopyFile> copyfiles, List<LinkFile> linkfiles, + Git git) throws GitAPIException, IOException { + assert (!repo.isBare()); + assert (git != null); + if (!linkfiles.isEmpty()) { + throw new UnsupportedOperationException( + JGitText.get().nonBareLinkFilesNotSupported); + } + + SubmoduleAddCommand add = git.submoduleAdd().setName(name).setPath(path) + .setURI(url); + if (monitor != null) { + add.setProgressMonitor(monitor); + } + + Repository subRepo = add.call(); + if (revision != null) { + try (Git sub = new Git(subRepo)) { + sub.checkout().setName(findRef(revision, subRepo)).call(); + } + subRepo.close(); + git.add().addFilepattern(path).call(); + } + for (CopyFile copyfile : copyfiles) { + copyfile.copy(); + git.add().addFilepattern(copyfile.dest).call(); + } + } + + private static String findRef(String ref, Repository repo) + throws IOException { + if (!ObjectId.isId(ref)) { + Ref r = repo.exactRef(R_REMOTES + DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$ + if (r != null) { + return r.getName(); + } + } + return ref; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java index e0a822479f..6e943e5d36 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java @@ -9,11 +9,6 @@ */ package org.eclipse.jgit.gitrepo; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME; -import static org.eclipse.jgit.lib.Constants.R_REMOTES; -import static org.eclipse.jgit.lib.Constants.R_TAGS; - import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -31,34 +26,21 @@ import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.GitCommand; -import org.eclipse.jgit.api.SubmoduleAddCommand; -import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRefNameException; -import org.eclipse.jgit.api.errors.JGitInternalException; -import org.eclipse.jgit.dircache.DirCache; -import org.eclipse.jgit.dircache.DirCacheBuilder; -import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.gitrepo.BareSuperprojectWriter.ExtraContent; import org.eclipse.jgit.gitrepo.ManifestParser.IncludedFileReader; -import org.eclipse.jgit.gitrepo.RepoProject.CopyFile; -import org.eclipse.jgit.gitrepo.RepoProject.LinkFile; import org.eclipse.jgit.gitrepo.internal.RepoText; import org.eclipse.jgit.internal.JGitText; -import org.eclipse.jgit.lib.CommitBuilder; -import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefDatabase; -import org.eclipse.jgit.lib.RefUpdate; -import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.util.FileUtils; @@ -80,12 +62,7 @@ import org.eclipse.jgit.util.FileUtils; * @since 3.4 */ public class RepoCommand extends GitCommand<RevCommit> { - private static final int LOCK_FAILURE_MAX_RETRIES = 5; - - // Retry exponentially with delays in this range - private static final int LOCK_FAILURE_MIN_RETRY_DELAY_MILLIS = 50; - private static final int LOCK_FAILURE_MAX_RETRY_DELAY_MILLIS = 5000; private String manifestPath; private String baseUri; @@ -93,17 +70,18 @@ public class RepoCommand extends GitCommand<RevCommit> { private String groupsParam; private String branch; private String targetBranch = Constants.HEAD; - private boolean recordRemoteBranch = true; - private boolean recordSubmoduleLabels = true; - private boolean recordShallowSubmodules = true; private PersonIdent author; private RemoteReader callback; private InputStream inputStream; private IncludedFileReader includedReader; - private boolean ignoreRemoteFailures = false; + + private BareSuperprojectWriter.BareWriterConfig bareWriterConfig = BareSuperprojectWriter.BareWriterConfig + .getDefault(); private ProgressMonitor monitor; + private final List<ExtraContent> extraContents = new ArrayList<>(); + /** * A callback to get ref sha1 of a repository from its uri. * @@ -269,14 +247,14 @@ public class RepoCommand extends GitCommand<RevCommit> { } @SuppressWarnings("serial") - private static class ManifestErrorException extends GitAPIException { + static class ManifestErrorException extends GitAPIException { ManifestErrorException(Throwable cause) { super(RepoText.get().invalidManifest, cause); } } @SuppressWarnings("serial") - private static class RemoteUnavailableException extends GitAPIException { + static class RemoteUnavailableException extends GitAPIException { RemoteUnavailableException(String uri) { super(MessageFormat.format(RepoText.get().errorRemoteUnavailable, uri)); } @@ -421,7 +399,7 @@ public class RepoCommand extends GitCommand<RevCommit> { * @since 4.2 */ public RepoCommand setRecordRemoteBranch(boolean enable) { - this.recordRemoteBranch = enable; + this.bareWriterConfig.recordRemoteBranch = enable; return this; } @@ -436,7 +414,7 @@ public class RepoCommand extends GitCommand<RevCommit> { * @since 4.4 */ public RepoCommand setRecordSubmoduleLabels(boolean enable) { - this.recordSubmoduleLabels = enable; + this.bareWriterConfig.recordSubmoduleLabels = enable; return this; } @@ -451,7 +429,7 @@ public class RepoCommand extends GitCommand<RevCommit> { * @since 4.4 */ public RepoCommand setRecommendShallow(boolean enable) { - this.recordShallowSubmodules = enable; + this.bareWriterConfig.recordShallowSubmodules = enable; return this; } @@ -485,7 +463,7 @@ public class RepoCommand extends GitCommand<RevCommit> { * @since 4.3 */ public RepoCommand setIgnoreRemoteFailures(boolean ignore) { - this.ignoreRemoteFailures = ignore; + this.bareWriterConfig.ignoreRemoteFailures = ignore; return this; } @@ -534,6 +512,22 @@ public class RepoCommand extends GitCommand<RevCommit> { return this; } + /** + * Create a file with the given content in the destination repository + * + * @param path + * where to create the file in the destination repository + * @param contents + * content for the create file + * @return this command + * + * @since 6.1 + */ + public RepoCommand addToDestination(String path, String contents) { + this.extraContents.add(new ExtraContent(path, contents)); + return this; + } + /** {@inheritDoc} */ @Override public RevCommit call() throws GitAPIException { @@ -570,240 +564,18 @@ public class RepoCommand extends GitCommand<RevCommit> { } if (repo.isBare()) { - if (author == null) - author = new PersonIdent(repo); - if (callback == null) - callback = new DefaultRemoteReader(); List<RepoProject> renamedProjects = renameProjects(filteredProjects); - - DirCache index = DirCache.newInCore(); - ObjectInserter inserter = repo.newObjectInserter(); - - try (RevWalk rw = new RevWalk(repo)) { - prepareIndex(renamedProjects, index, inserter); - ObjectId treeId = index.writeTree(inserter); - long prevDelay = 0; - for (int i = 0; i < LOCK_FAILURE_MAX_RETRIES - 1; i++) { - try { - return commitTreeOnCurrentTip( - inserter, rw, treeId); - } catch (ConcurrentRefUpdateException e) { - prevDelay = FileUtils.delay(prevDelay, - LOCK_FAILURE_MIN_RETRY_DELAY_MILLIS, - LOCK_FAILURE_MAX_RETRY_DELAY_MILLIS); - Thread.sleep(prevDelay); - repo.getRefDatabase().refresh(); - } - } - // In the last try, just propagate the exceptions - return commitTreeOnCurrentTip(inserter, rw, treeId); - } catch (IOException | InterruptedException e) { - throw new ManifestErrorException(e); - } - } - try (Git git = new Git(repo)) { - for (RepoProject proj : filteredProjects) { - addSubmodule(proj.getName(), proj.getUrl(), proj.getPath(), - proj.getRevision(), proj.getCopyFiles(), - proj.getLinkFiles(), git); - } - return git.commit().setMessage(RepoText.get().repoCommitMessage) - .call(); - } catch (IOException e) { - throw new ManifestErrorException(e); - } - } - - private void prepareIndex(List<RepoProject> projects, DirCache index, - ObjectInserter inserter) throws IOException, GitAPIException { - Config cfg = new Config(); - StringBuilder attributes = new StringBuilder(); - DirCacheBuilder builder = index.builder(); - for (RepoProject proj : projects) { - String name = proj.getName(); - String path = proj.getPath(); - String url = proj.getUrl(); - ObjectId objectId; - if (ObjectId.isId(proj.getRevision())) { - objectId = ObjectId.fromString(proj.getRevision()); - } else { - objectId = callback.sha1(url, proj.getRevision()); - if (objectId == null && !ignoreRemoteFailures) { - throw new RemoteUnavailableException(url); - } - if (recordRemoteBranch) { - // "branch" field is only for non-tag references. - // Keep tags in "ref" field as hint for other tools. - String field = proj.getRevision().startsWith(R_TAGS) ? "ref" //$NON-NLS-1$ - : "branch"; //$NON-NLS-1$ - cfg.setString("submodule", name, field, //$NON-NLS-1$ - proj.getRevision()); - } - - if (recordShallowSubmodules - && proj.getRecommendShallow() != null) { - // The shallow recommendation is losing information. - // As the repo manifests stores the recommended - // depth in the 'clone-depth' field, while - // git core only uses a binary 'shallow = true/false' - // hint, we'll map any depth to 'shallow = true' - cfg.setBoolean("submodule", name, "shallow", //$NON-NLS-1$ //$NON-NLS-2$ - true); - } - } - if (recordSubmoduleLabels) { - StringBuilder rec = new StringBuilder(); - rec.append("/"); //$NON-NLS-1$ - rec.append(path); - for (String group : proj.getGroups()) { - rec.append(" "); //$NON-NLS-1$ - rec.append(group); - } - rec.append("\n"); //$NON-NLS-1$ - attributes.append(rec.toString()); - } - - URI submodUrl = URI.create(url); - if (targetUri != null) { - submodUrl = relativize(targetUri, submodUrl); - } - cfg.setString("submodule", name, "path", path); //$NON-NLS-1$ //$NON-NLS-2$ - cfg.setString("submodule", name, "url", //$NON-NLS-1$ //$NON-NLS-2$ - submodUrl.toString()); - - // create gitlink - if (objectId != null) { - DirCacheEntry dcEntry = new DirCacheEntry(path); - dcEntry.setObjectId(objectId); - dcEntry.setFileMode(FileMode.GITLINK); - builder.add(dcEntry); - - for (CopyFile copyfile : proj.getCopyFiles()) { - RemoteFile rf = callback.readFileWithMode(url, - proj.getRevision(), copyfile.src); - objectId = inserter.insert(Constants.OBJ_BLOB, - rf.getContents()); - dcEntry = new DirCacheEntry(copyfile.dest); - dcEntry.setObjectId(objectId); - dcEntry.setFileMode(rf.getFileMode()); - builder.add(dcEntry); - } - for (LinkFile linkfile : proj.getLinkFiles()) { - String link; - if (linkfile.dest.contains("/")) { //$NON-NLS-1$ - link = FileUtils.relativizeGitPath( - linkfile.dest.substring(0, - linkfile.dest.lastIndexOf('/')), - proj.getPath() + "/" + linkfile.src); //$NON-NLS-1$ - } else { - link = proj.getPath() + "/" + linkfile.src; //$NON-NLS-1$ - } - - objectId = inserter.insert(Constants.OBJ_BLOB, - link.getBytes(UTF_8)); - dcEntry = new DirCacheEntry(linkfile.dest); - dcEntry.setObjectId(objectId); - dcEntry.setFileMode(FileMode.SYMLINK); - builder.add(dcEntry); - } - } - } - String content = cfg.toText(); - - // create a new DirCacheEntry for .gitmodules file. - DirCacheEntry dcEntry = new DirCacheEntry( - Constants.DOT_GIT_MODULES); - ObjectId objectId = inserter.insert(Constants.OBJ_BLOB, - content.getBytes(UTF_8)); - dcEntry.setObjectId(objectId); - dcEntry.setFileMode(FileMode.REGULAR_FILE); - builder.add(dcEntry); - - if (recordSubmoduleLabels) { - // create a new DirCacheEntry for .gitattributes file. - DirCacheEntry dcEntryAttr = new DirCacheEntry( - Constants.DOT_GIT_ATTRIBUTES); - ObjectId attrId = inserter.insert(Constants.OBJ_BLOB, - attributes.toString().getBytes(UTF_8)); - dcEntryAttr.setObjectId(attrId); - dcEntryAttr.setFileMode(FileMode.REGULAR_FILE); - builder.add(dcEntryAttr); - } - - builder.finish(); - } - - private RevCommit commitTreeOnCurrentTip(ObjectInserter inserter, - RevWalk rw, ObjectId treeId) - throws IOException, ConcurrentRefUpdateException { - ObjectId headId = repo.resolve(targetBranch + "^{commit}"); //$NON-NLS-1$ - if (headId != null && rw.parseCommit(headId).getTree().getId().equals(treeId)) { - // No change. Do nothing. - return rw.parseCommit(headId); + BareSuperprojectWriter writer = new BareSuperprojectWriter(repo, targetUri, + targetBranch, + author == null ? new PersonIdent(repo) : author, + callback == null ? new DefaultRemoteReader() : callback, + bareWriterConfig, extraContents); + return writer.write(renamedProjects); } - CommitBuilder commit = new CommitBuilder(); - commit.setTreeId(treeId); - if (headId != null) - commit.setParentIds(headId); - commit.setAuthor(author); - commit.setCommitter(author); - commit.setMessage(RepoText.get().repoCommitMessage); - - ObjectId commitId = inserter.insert(commit); - inserter.flush(); - - RefUpdate ru = repo.updateRef(targetBranch); - ru.setNewObjectId(commitId); - ru.setExpectedOldObjectId(headId != null ? headId : ObjectId.zeroId()); - Result rc = ru.update(rw); - switch (rc) { - case NEW: - case FORCED: - case FAST_FORWARD: - // Successful. Do nothing. - break; - case REJECTED: - case LOCK_FAILURE: - throw new ConcurrentRefUpdateException(MessageFormat - .format(JGitText.get().cannotLock, targetBranch), - ru.getRef(), rc); - default: - throw new JGitInternalException(MessageFormat.format( - JGitText.get().updatingRefFailed, - targetBranch, commitId.name(), rc)); - } - return rw.parseCommit(commitId); - } - - private void addSubmodule(String name, String url, String path, - String revision, List<CopyFile> copyfiles, List<LinkFile> linkfiles, - Git git) throws GitAPIException, IOException { - assert (!repo.isBare()); - assert (git != null); - if (!linkfiles.isEmpty()) { - throw new UnsupportedOperationException( - JGitText.get().nonBareLinkFilesNotSupported); - } - - SubmoduleAddCommand add = git.submoduleAdd().setName(name).setPath(path) - .setURI(url); - if (monitor != null) - add.setProgressMonitor(monitor); - - Repository subRepo = add.call(); - if (revision != null) { - try (Git sub = new Git(subRepo)) { - sub.checkout().setName(findRef(revision, subRepo)).call(); - } - subRepo.close(); - git.add().addFilepattern(path).call(); - } - for (CopyFile copyfile : copyfiles) { - copyfile.copy(); - git.add().addFilepattern(copyfile.dest).call(); - } + RegularSuperprojectWriter writer = new RegularSuperprojectWriter(repo, monitor); + return writer.write(filteredProjects); } /** @@ -910,13 +682,4 @@ public class RepoCommand extends GitCommand<RevCommit> { return URI.create(j.toString()); } - private static String findRef(String ref, Repository repo) - throws IOException { - if (!ObjectId.isId(ref)) { - Ref r = repo.exactRef(R_REMOTES + DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$ - if (r != null) - return r.getName(); - } - return ref; - } } 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 61a2e87671..2adde0a5d0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -387,6 +387,8 @@ public class JGitText extends TranslationBundle { /***/ public String inMemoryBufferLimitExceeded; /***/ public String inputDidntMatchLength; /***/ public String inputStreamMustSupportMark; + /***/ public String integerValueNotInRange; + /***/ public String integerValueNotInRangeSubSection; /***/ public String integerValueOutOfRange; /***/ public String internalRevisionError; /***/ public String internalServerError; @@ -398,6 +400,7 @@ public class JGitText extends TranslationBundle { /***/ public String invalidBooleanValue; /***/ public String invalidChannel; /***/ public String invalidCommitParentNumber; + /***/ public String invalidCoreAbbrev; /***/ public String invalidDepth; /***/ public String invalidEncoding; /***/ public String invalidEncryption; @@ -599,6 +602,11 @@ public class JGitText extends TranslationBundle { /***/ public String pushCertificateInvalidFieldValue; /***/ public String pushCertificateInvalidHeader; /***/ public String pushCertificateInvalidSignature; + /***/ public String pushDefaultNothing; + /***/ public String pushDefaultNoUpstream; + /***/ public String pushDefaultSimple; + /***/ public String pushDefaultTriangularUpstream; + /***/ public String pushDefaultUnknown; /***/ public String pushIsNotSupportedForBundleTransport; /***/ public String pushNotPermitted; /***/ public String pushOptionsNotSupported; 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 new file mode 100644 index 0000000000..509515c37a --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/CommandLineDiffTool.java @@ -0,0 +1,221 @@ +/* + * 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; + +/** + * Pre-defined command line diff tools. + * + * Adds same diff tools as also pre-defined in C-Git + * <p> + * see "git-core\mergetools\" + * </p> + * <p> + * see links to command line parameter description for the tools + * </p> + * + * <pre> + * araxis + * bc + * bc3 + * codecompare + * deltawalker + * diffmerge + * diffuse + * ecmerge + * emerge + * examdiff + * guiffy + * gvimdiff + * gvimdiff2 + * gvimdiff3 + * kdiff3 + * kompare + * meld + * opendiff + * p4merge + * tkdiff + * vimdiff + * vimdiff2 + * vimdiff3 + * winmerge + * xxdiff + * </pre> + * + */ +@SuppressWarnings("nls") +public enum CommandLineDiffTool { + /** + * 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 -2 \"$LOCAL\" \"$REMOTE\""), + /** + * 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\""), + /** + * 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?comparing_via_command_line.htm">https://www.devart.com/codecompare/docs/index.html?comparing_via_command_line.htm</a> + */ + codecompare("CodeCompare", "\"$LOCAL\" \"$REMOTE\""), + /** + * See: <a href= + * "https://www.deltawalker.com/integrate/command-line">https://www.deltawalker.com/integrate/command-line</a> + */ + deltawalker("DeltaWalker", "\"$LOCAL\" \"$REMOTE\""), + /** + * See: <a href= + * "https://sourcegear.com/diffmerge/webhelp/sec__clargs__diff.html">https://sourcegear.com/diffmerge/webhelp/sec__clargs__diff.html</a> + */ + diffmerge("diffmerge", "\"$LOCAL\" \"$REMOTE\""), + /** + * See: <a href= + * "http://diffuse.sourceforge.net/manual.html#introduction-usage">http://diffuse.sourceforge.net/manual.html#introduction-usage</a> + */ + diffuse("diffuse", "\"$LOCAL\" \"$REMOTE\""), + /** + * 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=diff2 \"$LOCAL\" \"$REMOTE\""), + /** + * 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> + */ + emerge("emacs", "-f emerge-files-command \"$LOCAL\" \"$REMOTE\""), + /** + * 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", "\"$LOCAL\" \"$REMOTE\" -nh"), + /** + * See: <a href= + * "https://www.guiffy.com/help/GuiffyHelp/GuiffyCmd.html">https://www.guiffy.com/help/GuiffyHelp/GuiffyCmd.html</a> + */ + guiffy("guiffy", "\"$LOCAL\" \"$REMOTE\""), + /** + * See: <a href= + * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> + */ + gvimdiff("gviewdiff", "\"$LOCAL\" \"$REMOTE\""), + /** + * See: <a href= + * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> + */ + gvimdiff2(gvimdiff), + /** + * See: <a href= + * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> + */ + gvimdiff3(gvimdiff), + /** + * See: <a href= + * "http://kdiff3.sourceforge.net/doc/documentation.html">http://kdiff3.sourceforge.net/doc/documentation.html</a> + */ + kdiff3("kdiff3", + "--L1 \"$MERGED (A)\" --L2 \"$MERGED (B)\" \"$LOCAL\" \"$REMOTE\""), + /** + * See: <a href= + * "https://docs.kde.org/trunk5/en/kdesdk/kompare/commandline-options.html">https://docs.kde.org/trunk5/en/kdesdk/kompare/commandline-options.html</a> + */ + kompare("kompare", "\"$LOCAL\" \"$REMOTE\""), + /** + * See: <a href= + * "ttp://meldmerge.org/help/file-mode.html">http://meldmerge.org/help/file-mode.html</a> + */ + meld("meld", "\"$LOCAL\" \"$REMOTE\""), + /** + * 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\""), + /** + * 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> + */ + p4merge("p4merge", "\"$LOCAL\" \"$REMOTE\""), + /** + * 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", "\"$LOCAL\" \"$REMOTE\""), + /** + * See: <a href= + * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> + */ + vimdiff("viewdiff", gvimdiff), + /** + * See: <a href= + * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> + */ + vimdiff2(vimdiff), + /** + * See: <a href= + * "http://vimdoc.sourceforge.net/htmldoc/diff.html">http://vimdoc.sourceforge.net/htmldoc/diff.html</a> + */ + vimdiff3(vimdiff), + /** + * 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 \"$LOCAL\" \"$REMOTE\""), + /** + * See: <a href= + * "http://furius.ca/xxdiff/doc/xxdiff-doc.html">http://furius.ca/xxdiff/doc/xxdiff-doc.html</a> + */ + xxdiff("xxdiff", + "-R 'Accel.Search: \"Ctrl+F\"' -R 'Accel.SearchForward: \"Ctrl+G\"' \"$LOCAL\" \"$REMOTE\""); + + CommandLineDiffTool(String path, String parameters) { + this.path = path; + this.parameters = parameters; + } + + CommandLineDiffTool(CommandLineDiffTool from) { + this(from.getPath(), from.getParameters()); + } + + CommandLineDiffTool(String path, CommandLineDiffTool from) { + this(path, from.getParameters()); + } + + private final String path; + + private final String parameters; + + /** + * @return path + */ + public String getPath() { + return path; + } + + /** + * @return parameters as one string + */ + public String getParameters() { + return parameters; + } + +} 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 new file mode 100644 index 0000000000..551f634f2d --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffToolConfig.java @@ -0,0 +1,117 @@ +/* + * 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 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 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 difftool related configuration options. + */ +public class DiffToolConfig { + + /** Key for {@link Config#get(SectionParser)}. */ + public static final Config.SectionParser<DiffToolConfig> KEY = DiffToolConfig::new; + + private final String toolName; + + private final String guiToolName; + + private final boolean prompt; + + private final BooleanTriState trustExitCode; + + private final Map<String, ExternalDiffTool> tools; + + private DiffToolConfig(Config rc) { + 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, + true); + String trustStr = rc.getString(CONFIG_DIFFTOOL_SECTION, null, + CONFIG_KEY_TRUST_EXIT_CODE); + if (trustStr != null) { + trustExitCode = Boolean.parseBoolean(trustStr) + ? BooleanTriState.TRUE + : BooleanTriState.FALSE; + } else { + trustExitCode = BooleanTriState.UNSET; + } + tools = new HashMap<>(); + Set<String> subsections = rc.getSubsections(CONFIG_DIFFTOOL_SECTION); + for (String name : subsections) { + String cmd = rc.getString(CONFIG_DIFFTOOL_SECTION, name, + CONFIG_KEY_CMD); + String path = rc.getString(CONFIG_DIFFTOOL_SECTION, name, + CONFIG_KEY_PATH); + if ((cmd != null) || (path != null)) { + tools.put(name, new UserDefinedDiffTool(name, path, cmd)); + } + } + } + + /** + * @return the default diff tool name (diff.tool) + */ + public String getDefaultToolName() { + return toolName; + } + + /** + * @return the default GUI diff tool name (diff.guitool) + */ + public String getDefaultGuiToolName() { + return guiToolName; + } + + /** + * @return the diff tool "prompt" option (difftool.prompt) + */ + public boolean isPrompt() { + return prompt; + } + + /** + * @return the diff tool "trust exit code" option (difftool.trustExitCode) + */ + public boolean isTrustExitCode() { + return trustExitCode == BooleanTriState.TRUE; + } + + /** + * @return the tools map + */ + public Map<String, ExternalDiffTool> 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/DiffTools.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java new file mode 100644 index 0000000000..39729a4eec --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/DiffTools.java @@ -0,0 +1,143 @@ +/* + * 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.util.Collections; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.internal.BooleanTriState; + +/** + * Manages diff tools. + */ +public class DiffTools { + + private final DiffToolConfig config; + + private Map<String, ExternalDiffTool> predefinedTools; + + private Map<String, ExternalDiffTool> userDefinedTools; + + /** + * Creates the external diff-tools manager for given repository. + * + * @param repo + * the repository + */ + public DiffTools(Repository repo) { + config = repo.getConfig().get(DiffToolConfig.KEY); + setupPredefinedTools(); + setupUserDefinedTools(); + } + + /** + * 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 toolName + * the selected tool name (can be null) + * @param prompt + * the prompt option + * @param gui + * the GUI option + * @param trustExitCode + * the "trust exit code" option + * @return the return code from executed tool + */ + public int compare(String newPath, String oldPath, String newId, + String oldId, String toolName, BooleanTriState prompt, + BooleanTriState gui, BooleanTriState trustExitCode) { + return 0; + } + + /** + * @return the tool names + */ + public Set<String> getToolNames() { + return config.getToolNames(); + } + + /** + * @return the user defined tools + */ + public Map<String, ExternalDiffTool> getUserDefinedTools() { + return Collections.unmodifiableMap(userDefinedTools); + } + + /** + * @return the available predefined tools + */ + public Map<String, ExternalDiffTool> getAvailableTools() { + return Collections.unmodifiableMap(predefinedTools); + } + + /** + * @return the NOT available predefined tools + */ + public Map<String, ExternalDiffTool> getNotAvailableTools() { + return Collections.unmodifiableMap(new TreeMap<>()); + } + + /** + * @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$ + } + + /** + * @return is interactive (config prompt enabled) ? + */ + public boolean isInteractive() { + return false; + } + + private void setupPredefinedTools() { + predefinedTools = new TreeMap<>(); + for (CommandLineDiffTool tool : CommandLineDiffTool.values()) { + predefinedTools.put(tool.name(), new PreDefinedDiffTool(tool)); + } + } + + private void setupUserDefinedTools() { + userDefinedTools = 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); + } else if (userTool.getPath() != null) { + // if difftool.<name>.path is defined we just overload the path + // of predefined tool + PreDefinedDiffTool predefTool = (PreDefinedDiffTool) predefinedTools + .get(name); + if (predefTool != null) { + predefTool.setPath(userTool.getPath()); + } + } + } + } + +} 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 new file mode 100644 index 0000000000..f2d7e828cb --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/ExternalDiffTool.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * The external tool interface. + */ +public interface ExternalDiffTool { + + /** + * @return the tool name + */ + String getName(); + + /** + * @return the tool path + */ + String getPath(); + + /** + * @return the tool command + */ + String getCommand(); + +} 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 new file mode 100644 index 0000000000..1c69fb4911 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/PreDefinedDiffTool.java @@ -0,0 +1,73 @@ +/* + * 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; + +/** + * The pre-defined diff tool. + */ +public class PreDefinedDiffTool extends UserDefinedDiffTool { + + /** + * Create a pre-defined diff tool + * + * @param name + * the name + * @param path + * the path + * @param parameters + * the tool parameters as one string that is used together with + * path as command + */ + public PreDefinedDiffTool(String name, String path, String parameters) { + super(name, path, parameters); + } + + /** + * Creates the pre-defined diff tool + * + * @param tool + * the command line diff tool + * + */ + public PreDefinedDiffTool(CommandLineDiffTool tool) { + this(tool.name(), tool.getPath(), tool.getParameters()); + } + + /** + * @param path + */ + @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); + } + + /** + * {@inheritDoc} + * + * @return the concatenated path and command of the pre-defined diff tool + */ + @Override + public String getCommand() { + return getPath() + " " + super.getCommand(); //$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 new file mode 100644 index 0000000000..012296eb35 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diffmergetool/UserDefinedDiffTool.java @@ -0,0 +1,114 @@ +/* + * 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; + +/** + * The user-defined diff tool. + */ +public class UserDefinedDiffTool implements ExternalDiffTool { + + /** + * the diff tool name + */ + private final String name; + + /** + * the diff tool path + */ + private String path; + + /** + * the diff tool command + */ + private final String cmd; + + /** + * Creates the diff tool + * + * @param name + * the name + * @param path + * the path + * @param cmd + * the command + */ + public UserDefinedDiffTool(String name, String path, String cmd) { + this.name = name; + this.path = path; + this.cmd = cmd; + } + + /** + * @return the diff tool name + */ + @Override + public String getName() { + return name; + } + + /** + * The path of the diff tool. + * + * <p> + * The path to a pre-defined external diff tool can be overridden by + * specifying {@code difftool.<tool>.path} in a configuration file. + * </p> + * <p> + * For a user defined diff tool (that does not override a pre-defined diff + * tool), the path is ignored when invoking the tool. + * </p> + * + * @return the diff tool path + * + * @see <a href= + * "https://git-scm.com/docs/git-difftool">https://git-scm.com/docs/git-difftool</a> + */ + @Override + public String getPath() { + return path; + } + + /** + * The command of the diff tool. + * + * <p> + * A pre-defined external diff tool can be overridden using the tools name + * in a configuration file. The overwritten tool is then a user defined tool + * and the command of the diff tool is specified with + * {@code difftool.<tool>.cmd}. This command must work without prepending + * the value of {@link #getPath()} and can sometimes include tool + * parameters. + * </p> + * + * @return the diff tool command + * + * @see <a href= + * "https://git-scm.com/docs/git-difftool">https://git-scm.com/docs/git-difftool</a> + */ + @Override + public String getCommand() { + return cmd; + } + + /** + * Overrides the path for the given tool. Equivalent to setting + * {@code difftool.<tool>.path}. + * + * @param path + * the new diff tool path + * + * @see <a href= + * "https://git-scm.com/docs/git-difftool">https://git-scm.com/docs/git-difftool</a> + */ + public void setPath(String path) { + this.path = path; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java index 54c527c03c..b30d50921a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java @@ -12,6 +12,10 @@ package org.eclipse.jgit.internal.storage.dfs; import java.io.IOException; +import java.time.Duration; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReferenceArray; @@ -166,6 +170,12 @@ public final class DfsBlockCache { /** Limits of cache hot count per pack file extension. */ private final int[] cacheHotLimits = new int[PackExt.values().length]; + /** Consumer of loading and eviction events of indexes. */ + private final DfsBlockCacheConfig.IndexEventConsumer indexEventConsumer; + + /** Stores timestamps of the last eviction of indexes. */ + private final Map<EvictKey, Long> indexEvictionMap = new ConcurrentHashMap<>(); + @SuppressWarnings("unchecked") private DfsBlockCache(DfsBlockCacheConfig cfg) { tableSize = tableSize(cfg); @@ -213,6 +223,7 @@ public final class DfsBlockCache { cacheHotLimits[i] = DfsBlockCacheConfig.DEFAULT_CACHE_HOT_MAX; } } + indexEventConsumer = cfg.getIndexEventConsumer(); } boolean shouldCopyThroughCache(long length) { @@ -461,6 +472,7 @@ public final class DfsBlockCache { live -= dead.size; getStat(liveBytes, dead.key).addAndGet(-dead.size); getStat(statEvict, dead.key).incrementAndGet(); + reportIndexEvicted(dead); } while (maxBytes < live); clockHand = prev; } @@ -515,11 +527,13 @@ public final class DfsBlockCache { <T> Ref<T> getOrLoadRef( DfsStreamKey key, long position, RefLoader<T> loader) throws IOException { + long start = System.nanoTime(); int slot = slot(key, position); HashEntry e1 = table.get(slot); Ref<T> ref = scanRef(e1, key, position); if (ref != null) { getStat(statHit, key).incrementAndGet(); + reportIndexRequested(ref, true /* cacheHit */, start); return ref; } @@ -532,6 +546,8 @@ public final class DfsBlockCache { ref = scanRef(e2, key, position); if (ref != null) { getStat(statHit, key).incrementAndGet(); + reportIndexRequested(ref, true /* cacheHit */, + start); return ref; } } @@ -556,6 +572,7 @@ public final class DfsBlockCache { } finally { regionLock.unlock(); } + reportIndexRequested(ref, false /* cacheHit */, start); return ref; } @@ -682,8 +699,9 @@ public final class DfsBlockCache { } private static HashEntry clean(HashEntry top) { - while (top != null && top.ref.next == null) + while (top != null && top.ref.next == null) { top = top.next; + } if (top == null) { return null; } @@ -691,6 +709,44 @@ public final class DfsBlockCache { return n == top.next ? top : new HashEntry(n, top.ref); } + private void reportIndexRequested(Ref<?> ref, boolean cacheHit, + long start) { + if (indexEventConsumer == null + || !isIndexOrBitmapExtPos(ref.key.packExtPos)) { + return; + } + EvictKey evictKey = new EvictKey(ref); + Long prevEvictedTime = indexEvictionMap.get(evictKey); + long now = System.nanoTime(); + long sinceLastEvictionNanos = prevEvictedTime == null ? 0L + : now - prevEvictedTime.longValue(); + indexEventConsumer.acceptRequestedEvent(ref.key.packExtPos, cacheHit, + (now - start) / 1000L /* micros */, ref.size, + Duration.ofNanos(sinceLastEvictionNanos)); + } + + private void reportIndexEvicted(Ref<?> dead) { + if (indexEventConsumer == null + || !indexEventConsumer.shouldReportEvictedEvent() + || !isIndexOrBitmapExtPos(dead.key.packExtPos)) { + return; + } + EvictKey evictKey = new EvictKey(dead); + Long prevEvictedTime = indexEvictionMap.get(evictKey); + long now = System.nanoTime(); + long sinceLastEvictionNanos = prevEvictedTime == null ? 0L + : now - prevEvictedTime.longValue(); + indexEvictionMap.put(evictKey, Long.valueOf(now)); + indexEventConsumer.acceptEvictedEvent(dead.key.packExtPos, dead.size, + dead.totalHitCount.get(), + Duration.ofNanos(sinceLastEvictionNanos)); + } + + private static boolean isIndexOrBitmapExtPos(int packExtPos) { + return packExtPos == PackExt.INDEX.getPosition() + || packExtPos == PackExt.BITMAP_INDEX.getPosition(); + } + private static final class HashEntry { /** Next entry in the hash table's chain list. */ final HashEntry next; @@ -712,6 +768,7 @@ public final class DfsBlockCache { Ref next; private volatile int hotCount; + private AtomicInteger totalHitCount = new AtomicInteger(); Ref(DfsStreamKey key, long position, long size, T v) { this.key = key; @@ -736,6 +793,7 @@ public final class DfsBlockCache { int cap = DfsBlockCache .getInstance().cacheHotLimits[key.packExtPos]; hotCount = Math.min(cap, hotCount + 1); + totalHitCount.incrementAndGet(); } void markColder() { @@ -747,6 +805,34 @@ public final class DfsBlockCache { } } + private static final class EvictKey { + private final int keyHash; + private final int packExtPos; + private final long position; + + EvictKey(Ref<?> ref) { + keyHash = ref.key.hash; + packExtPos = ref.key.packExtPos; + position = ref.position; + } + + @Override + public boolean equals(Object object) { + if (object instanceof EvictKey) { + EvictKey other = (EvictKey) object; + return keyHash == other.keyHash + && packExtPos == other.packExtPos + && position == other.position; + } + return false; + } + + @Override + public int hashCode() { + return DfsBlockCache.getInstance().hash(keyHash, position); + } + } + @FunctionalInterface interface RefLoader<T> { Ref<T> load() throws IOException; @@ -763,4 +849,4 @@ public final class DfsBlockCache { */ ReadableChannel get() throws IOException; } -} +}
\ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java index 2716f79a1a..69a37058bf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java @@ -18,6 +18,7 @@ import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONCURRENCY_LEVEL; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_RATIO; import java.text.MessageFormat; +import java.time.Duration; import java.util.Collections; import java.util.Map; import java.util.function.Consumer; @@ -46,9 +47,10 @@ public class DfsBlockCacheConfig { private int concurrencyLevel; private Consumer<Long> refLock; - private Map<PackExt, Integer> cacheHotMap; + private IndexEventConsumer indexEventConsumer; + /** * Create a default configuration. */ @@ -216,6 +218,28 @@ public class DfsBlockCacheConfig { } /** + * Get the consumer of cache index events. + * + * @return consumer of cache index events. + */ + public IndexEventConsumer getIndexEventConsumer() { + return indexEventConsumer; + } + + /** + * Set the consumer of cache index events. + * + * @param indexEventConsumer + * consumer of cache index events. + * @return {@code this} + */ + public DfsBlockCacheConfig setIndexEventConsumer( + IndexEventConsumer indexEventConsumer) { + this.indexEventConsumer = indexEventConsumer; + return this; + } + + /** * Update properties by setting fields from the configuration. * <p> * If a property is not defined in the configuration, then it is left @@ -272,4 +296,52 @@ public class DfsBlockCacheConfig { } return this; } -} + + /** Consumer of DfsBlockCache loading and eviction events for indexes. */ + public interface IndexEventConsumer { + /** + * Accept an event of an index requested. It could be loaded from either + * cache or storage. + * + * @param packExtPos + * position in {@code PackExt} enum + * @param cacheHit + * true if an index was already in cache. Otherwise, the + * index was loaded from storage into the cache in the + * current request, + * @param loadMicros + * time to load an index from cache or storage in + * microseconds + * @param bytes + * number of bytes loaded + * @param lastEvictionDuration + * time since last eviction, 0 if was not evicted yet + */ + void acceptRequestedEvent(int packExtPos, boolean cacheHit, + long loadMicros, long bytes, Duration lastEvictionDuration); + + /** + * Accept an event of an index evicted from cache. + * + * @param packExtPos + * position in {@code PackExt} enum + * @param bytes + * number of bytes evicted + * @param totalCacheHitCount + * number of times an index was accessed while in cache + * @param lastEvictionDuration + * time since last eviction, 0 if was not evicted yet + */ + default void acceptEvictedEvent(int packExtPos, long bytes, + int totalCacheHitCount, Duration lastEvictionDuration) { + // Off by default. + } + + /** + * @return true if reporting evicted events is enabled. + */ + default boolean shouldReportEvictedEvent() { + return false; + } + } +}
\ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java index bb76df1d5d..f7a2c94d48 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java @@ -1061,7 +1061,8 @@ public final class DfsPackFile extends BlockBasedFile { } in = new BufferedInputStream(in, bs); bmidx = PackBitmapIndex.read(in, () -> idx(ctx), - () -> getReverseIdx(ctx)); + () -> getReverseIdx(ctx), + ctx.getOptions().shouldLoadRevIndexInParallel()); } finally { size = rc.position(); ctx.stats.readBitmapIdxBytes += size; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java index 89de53460c..146f76167d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java @@ -34,6 +34,8 @@ public class DfsReaderOptions { private int streamPackBufferSize; + private boolean loadRevIndexInParallel; + /** * Create a default reader configuration. */ @@ -113,6 +115,28 @@ public class DfsReaderOptions { } /** + * Check if reverse index should be loaded in parallel. + * + * @return true if reverse index is loaded in parallel for bitmap index. + */ + public boolean shouldLoadRevIndexInParallel() { + return loadRevIndexInParallel; + } + + /** + * Enable (or disable) parallel loading of reverse index. + * + * @param loadRevIndexInParallel + * whether to load reverse index in parallel. + * @return {@code this} + */ + public DfsReaderOptions setLoadRevIndexInParallel( + boolean loadRevIndexInParallel) { + this.loadRevIndexInParallel = loadRevIndexInParallel; + return this; + } + + /** * Update properties by setting fields from the configuration. * <p> * If a property is not defined in the configuration, then it is left diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java index 5b6894da9c..99da222395 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java @@ -165,6 +165,15 @@ public class InMemoryRepository extends DfsRepository { } }; } + + @Override + public long getApproximateObjectCount() { + long count = 0; + for (DfsPackDescription p : packs) { + count += p.getObjectCount(); + } + return count; + } } private static class MemPack extends DfsPackDescription { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java index b0612f9395..cd4f168d86 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java @@ -73,7 +73,6 @@ final class LargePackedWholeObject extends ObjectLoader { public ObjectStream openStream() throws MissingObjectException, IOException { PackInputStream packIn; // ctx is closed by PackInputStream, or explicitly in the finally block - @SuppressWarnings("resource") DfsReader ctx = db.newReader(); try { try { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java index 7dedeb57ab..094fdc1559 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java @@ -263,4 +263,17 @@ class CachedObjectDirectory extends FileObjectDatabase { private AlternateHandle.Id getAlternateId() { return wrapped.getAlternateId(); } + + @Override + public long getApproximateObjectCount() { + long count = 0; + for (Pack p : getPacks()) { + try { + count += p.getObjectCount(); + } catch (IOException e) { + return -1; + } + } + return count; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java index f02c8613a0..5152367d23 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java @@ -40,6 +40,7 @@ import org.eclipse.jgit.internal.storage.reftable.ReftableReader; import org.eclipse.jgit.internal.storage.reftable.ReftableWriter; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.util.FileUtils; +import org.eclipse.jgit.util.SystemReader; /** * A mutable stack of reftables on local filesystem storage. Not thread-safe. @@ -527,11 +528,19 @@ public class FileReftableStack implements AutoCloseable { return false; } + reload(); for (File f : deleteOnSuccess) { - Files.delete(f.toPath()); + try { + Files.delete(f.toPath()); + } catch (IOException e) { + // Ignore: this can happen on Windows in case of concurrent processes. + // leave the garbage and continue. + if (!SystemReader.getInstance().isWindows()) { + throw e; + } + } } - reload(); return true; } finally { if (tmpTable != null) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java index b37155717c..3e92cddacd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java @@ -672,18 +672,20 @@ public class FileRepository extends Repository { if (writeLogs) { List<ReflogEntry> logs = oldDb.getReflogReader(r.getName()) - .getReverseEntries(); + .getReverseEntries(); Collections.reverse(logs); for (ReflogEntry e : logs) { logWriter.log(r.getName(), e); } - } + } } try (RevWalk rw = new RevWalk(this)) { bru.execute(rw, NullProgressMonitor.INSTANCE); } + oldDb.close(); + List<String> failed = new ArrayList<>(); for (ReceiveCommand cmd : bru.getCommands()) { if (cmd.getResult() != ReceiveCommand.Result.OK) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java index 33621a1e9f..b9af83d24d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java @@ -14,6 +14,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.StandardCopyOption; @@ -24,6 +25,8 @@ import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.file.FileObjectDatabase.InsertLooseObjectResult; import org.eclipse.jgit.lib.AbbreviatedObjectId; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; @@ -52,15 +55,22 @@ class LooseObjects { private final UnpackedObjectCache unpackedObjectCache; + private final boolean trustFolderStat; + /** * Initialize a reference to an on-disk object directory. * + * @param config + * configuration for the loose objects handler. * @param dir * the location of the <code>objects</code> directory. */ - LooseObjects(File dir) { + LooseObjects(Config config, File dir) { directory = dir; unpackedObjectCache = new UnpackedObjectCache(); + trustFolderStat = config.getBoolean( + ConfigConstants.CONFIG_CORE_SECTION, + ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true); } /** @@ -98,6 +108,19 @@ class LooseObjects { * @return {@code true} if the specified object is stored as a loose object. */ boolean has(AnyObjectId objectId) { + boolean exists = hasWithoutRefresh(objectId); + if (trustFolderStat || exists) { + return exists; + } + try (InputStream stream = Files.newInputStream(directory.toPath())) { + // refresh directory to work around NFS caching issue + } catch (IOException e) { + return false; + } + return hasWithoutRefresh(objectId); + } + + private boolean hasWithoutRefresh(AnyObjectId objectId) { return fileFor(objectId).exists(); } @@ -183,6 +206,22 @@ class LooseObjects { */ ObjectLoader getObjectLoader(WindowCursor curs, File path, AnyObjectId id) throws IOException { + try { + return getObjectLoaderWithoutRefresh(curs, path, id); + } catch (FileNotFoundException e) { + if (trustFolderStat) { + throw e; + } + try (InputStream stream = Files + .newInputStream(directory.toPath())) { + // refresh directory to work around NFS caching issues + } + return getObjectLoaderWithoutRefresh(curs, path, id); + } + } + + private ObjectLoader getObjectLoaderWithoutRefresh(WindowCursor curs, + File path, AnyObjectId id) throws IOException { try (FileInputStream in = new FileInputStream(path)) { unpackedObjectCache().add(id); return UnpackedObject.open(in, path, id, curs); @@ -203,16 +242,34 @@ class LooseObjects { } long getSize(WindowCursor curs, AnyObjectId id) throws IOException { + try { + return getSizeWithoutRefresh(curs, id); + } catch (FileNotFoundException noFile) { + try { + if (trustFolderStat) { + throw noFile; + } + try (InputStream stream = Files + .newInputStream(directory.toPath())) { + // refresh directory to work around NFS caching issue + } + return getSizeWithoutRefresh(curs, id); + } catch (FileNotFoundException e) { + if (fileFor(id).exists()) { + throw noFile; + } + unpackedObjectCache().remove(id); + return -1; + } + } + } + + private long getSizeWithoutRefresh(WindowCursor curs, AnyObjectId id) + throws IOException { File f = fileFor(id); try (FileInputStream in = new FileInputStream(f)) { unpackedObjectCache().add(id); return UnpackedObject.getSize(in, id, curs); - } catch (FileNotFoundException noFile) { - if (f.exists()) { - throw noFile; - } - unpackedObjectCache().remove(id); - return -1; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java index a3ce3158ae..ac3a553279 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java @@ -121,7 +121,7 @@ public class ObjectDirectory extends FileObjectDatabase { File packDirectory = new File(objects, "pack"); //$NON-NLS-1$ File preservedDirectory = new File(packDirectory, "preserved"); //$NON-NLS-1$ alternatesFile = new File(objects, Constants.INFO_ALTERNATES); - loose = new LooseObjects(objects); + loose = new LooseObjects(config, objects); packed = new PackDirectory(config, packDirectory); preserved = new PackDirectory(config, preservedDirectory); this.fs = fs; @@ -213,6 +213,20 @@ public class ObjectDirectory extends FileObjectDatabase { return packed.getPacks(); } + /** {@inheritDoc} */ + @Override + public long getApproximateObjectCount() { + long count = 0; + for (Pack p : getPacks()) { + try { + count += p.getIndex().getObjectCount(); + } catch (IOException e) { + return -1; + } + } + return count; + } + /** * {@inheritDoc} * <p> diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java index 8401f0718a..8fb17fcf21 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java @@ -95,7 +95,7 @@ public abstract class PackBitmapIndex { */ public static PackBitmapIndex read(InputStream fd, PackIndex packIndex, PackReverseIndex reverseIndex) throws IOException { - return new PackBitmapIndexV1(fd, () -> packIndex, () -> reverseIndex); + return new PackBitmapIndexV1(fd, packIndex, reverseIndex); } /** @@ -114,6 +114,8 @@ public abstract class PackBitmapIndex { * @param reverseIndexSupplier * the supplier for pack reverse index for the corresponding pack * file. + * @param loadParallelRevIndex + * whether reverse index should be loaded in parallel * @return a copy of the index in-memory. * @throws java.io.IOException * the stream cannot be read. @@ -122,10 +124,11 @@ public abstract class PackBitmapIndex { */ public static PackBitmapIndex read(InputStream fd, SupplierWithIOException<PackIndex> packIndexSupplier, - SupplierWithIOException<PackReverseIndex> reverseIndexSupplier) + SupplierWithIOException<PackReverseIndex> reverseIndexSupplier, + boolean loadParallelRevIndex) throws IOException { return new PackBitmapIndexV1(fd, packIndexSupplier, - reverseIndexSupplier); + reverseIndexSupplier, loadParallelRevIndex); } /** Footer checksum applied on the bottom of the pack file. */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java index 6846e3bcad..21aba3e6a3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java @@ -17,6 +17,12 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.internal.JGitText; @@ -40,6 +46,23 @@ class PackBitmapIndexV1 extends BasePackBitmapIndex { private static final int MAX_XOR_OFFSET = 126; + private static final ExecutorService executor = Executors + .newCachedThreadPool(new ThreadFactory() { + private final ThreadFactory baseFactory = Executors + .defaultThreadFactory(); + + private final AtomicInteger threadNumber = new AtomicInteger(0); + + @Override + public Thread newThread(Runnable runnable) { + Thread thread = baseFactory.newThread(runnable); + thread.setName("JGit-PackBitmapIndexV1-" //$NON-NLS-1$ + + threadNumber.getAndIncrement()); + thread.setDaemon(true); + return thread; + } + }); + private final PackIndex packIndex; private final PackReverseIndex reverseIndex; private final EWAHCompressedBitmap commits; @@ -49,15 +72,28 @@ class PackBitmapIndexV1 extends BasePackBitmapIndex { private final ObjectIdOwnerMap<StoredBitmap> bitmaps; + PackBitmapIndexV1(final InputStream fd, PackIndex packIndex, + PackReverseIndex reverseIndex) throws IOException { + this(fd, () -> packIndex, () -> reverseIndex, false); + } + PackBitmapIndexV1(final InputStream fd, SupplierWithIOException<PackIndex> packIndexSupplier, - SupplierWithIOException<PackReverseIndex> reverseIndexSupplier) + SupplierWithIOException<PackReverseIndex> reverseIndexSupplier, + boolean loadParallelRevIndex) throws IOException { // An entry is object id, xor offset, flag byte, and a length encoded // bitmap. The object id is an int32 of the nth position sorted by name. super(new ObjectIdOwnerMap<StoredBitmap>()); this.bitmaps = getBitmaps(); + // Optionally start loading reverse index in parallel to loading bitmap + // from storage. + Future<PackReverseIndex> reverseIndexFuture = null; + if (loadParallelRevIndex) { + reverseIndexFuture = executor.submit(reverseIndexSupplier::get); + } + final byte[] scratch = new byte[32]; IO.readFully(fd, scratch, 0, scratch.length); @@ -164,7 +200,18 @@ class PackBitmapIndexV1 extends BasePackBitmapIndex { bitmaps.add(sb); } - this.reverseIndex = reverseIndexSupplier.get(); + PackReverseIndex computedReverseIndex; + if (loadParallelRevIndex && reverseIndexFuture != null) { + try { + computedReverseIndex = reverseIndexFuture.get(); + } catch (InterruptedException | ExecutionException e) { + // Fallback to loading reverse index through a supplier. + computedReverseIndex = reverseIndexSupplier.get(); + } + } else { + computedReverseIndex = reverseIndexSupplier.get(); + } + this.reverseIndex = computedReverseIndex; } /** {@inheritDoc} */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java index 22977d3c03..6a99cb3d83 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java @@ -65,12 +65,12 @@ class PackDirectory { private static final PackList NO_PACKS = new PackList(FileSnapshot.DIRTY, new Pack[0]); - private final Config config; - private final File directory; private final AtomicReference<PackList> packList; + private final boolean trustFolderStat; + /** * Initialize a reference to an on-disk 'pack' directory. * @@ -80,9 +80,16 @@ class PackDirectory { * the location of the {@code pack} directory. */ PackDirectory(Config config, File directory) { - this.config = config; this.directory = directory; packList = new AtomicReference<>(NO_PACKS); + + // Whether to trust the pack folder's modification time. If set to false + // we will always scan the .git/objects/pack folder to check for new + // pack files. If set to true (default) we use the folder's size, + // modification time, and key (inode) and assume that no new pack files + // can be in this folder if these attributes have not changed. + trustFolderStat = config.getBoolean(ConfigConstants.CONFIG_CORE_SECTION, + ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true); } /** @@ -350,16 +357,6 @@ class PackDirectory { } boolean searchPacksAgain(PackList old) { - // Whether to trust the pack folder's modification time. If set - // to false we will always scan the .git/objects/pack folder to - // check for new pack files. If set to true (default) we use the - // lastmodified attribute of the folder and assume that no new - // pack files can be in this folder if his modification time has - // not changed. - boolean trustFolderStat = config.getBoolean( - ConfigConstants.CONFIG_CORE_SECTION, - ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true); - return ((!trustFolderStat) || old.snapshot.isModified(directory)) && old != scanPacks(old); } 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 fbf0ae9cfa..1db4f748f1 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 @@ -30,10 +30,12 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.nio.file.DirectoryNotEmptyException; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.security.DigestInputStream; import java.security.MessageDigest; @@ -60,6 +62,7 @@ import org.eclipse.jgit.events.RefsChangedEvent; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.CoreConfig.TrustPackedRefsStat; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; import org.eclipse.jgit.lib.Ref; @@ -177,6 +180,10 @@ public class RefDirectory extends RefDatabase { private List<Integer> retrySleepMs = RETRY_SLEEP_MS; + private final boolean trustFolderStat; + + private final TrustPackedRefsStat trustPackedRefsStat; + RefDirectory(FileRepository db) { final FS fs = db.getFS(); parent = db; @@ -188,6 +195,13 @@ public class RefDirectory extends RefDatabase { looseRefs.set(RefList.<LooseRef> emptyList()); packedRefs.set(NO_PACKED_REFS); + trustFolderStat = db.getConfig() + .getBoolean(ConfigConstants.CONFIG_CORE_SECTION, + ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true); + trustPackedRefsStat = db.getConfig() + .getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_TRUST_PACKED_REFS_STAT, + TrustPackedRefsStat.UNSET); } Repository getRepository() { @@ -889,13 +903,30 @@ public class RefDirectory extends RefDatabase { } PackedRefList getPackedRefs() throws IOException { - boolean trustFolderStat = getRepository().getConfig().getBoolean( - ConfigConstants.CONFIG_CORE_SECTION, - ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true); - final PackedRefList curList = packedRefs.get(); - if (trustFolderStat && !curList.snapshot.isModified(packedRefsFile)) { - return curList; + + switch (trustPackedRefsStat) { + case NEVER: + break; + case AFTER_OPEN: + try (InputStream stream = Files + .newInputStream(packedRefsFile.toPath())) { + // open the file to refresh attributes (on some NFS clients) + } catch (FileNotFoundException | NoSuchFileException e) { + // Ignore as packed-refs may not exist + } + //$FALL-THROUGH$ + case ALWAYS: + if (!curList.snapshot.isModified(packedRefsFile)) { + return curList; + } + break; + case UNSET: + if (trustFolderStat + && !curList.snapshot.isModified(packedRefsFile)) { + return curList; + } + break; } final PackedRefList newList = readPackedRefs(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/io/CancellableDigestOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/io/CancellableDigestOutputStream.java new file mode 100644 index 0000000000..ca2095feec --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/io/CancellableDigestOutputStream.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2022, Tencent. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.internal.storage.io; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ProgressMonitor; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.security.MessageDigest; + +/** + * An OutputStream that keeps a digest and checks every N bytes for + * cancellation. + */ +public class CancellableDigestOutputStream extends OutputStream { + + /** The OutputStream checks every this value for cancellation **/ + public static final int BYTES_TO_WRITE_BEFORE_CANCEL_CHECK = 128 * 1024; + + private final ProgressMonitor writeMonitor; + + private final OutputStream out; + + private final MessageDigest md = Constants.newMessageDigest(); + + private long count; + + private long checkCancelAt; + + /** + * Initialize a CancellableDigestOutputStream. + * + * @param writeMonitor + * monitor to update on output progress and check cancel. + * @param out + * target stream to receive all contents. + */ + public CancellableDigestOutputStream(ProgressMonitor writeMonitor, + OutputStream out) { + this.writeMonitor = writeMonitor; + this.out = out; + this.checkCancelAt = BYTES_TO_WRITE_BEFORE_CANCEL_CHECK; + } + + /** + * Get the monitor which is used to update on output progress and check + * cancel. + * + * @return the monitor + */ + public final ProgressMonitor getWriteMonitor() { + return writeMonitor; + } + + /** + * Obtain the current SHA-1 digest. + * + * @return SHA-1 digest + */ + public final byte[] getDigest() { + return md.digest(); + } + + /** + * Get total number of bytes written since stream start. + * + * @return total number of bytes written since stream start. + */ + public final long length() { + return count; + } + + /** {@inheritDoc} */ + @Override + public final void write(int b) throws IOException { + if (checkCancelAt <= count) { + if (writeMonitor.isCancelled()) { + throw new InterruptedIOException(); + } + checkCancelAt = count + BYTES_TO_WRITE_BEFORE_CANCEL_CHECK; + } + + out.write(b); + md.update((byte) b); + count++; + } + + /** {@inheritDoc} */ + @Override + public final void write(byte[] b, int off, int len) throws IOException { + while (0 < len) { + if (checkCancelAt <= count) { + if (writeMonitor.isCancelled()) { + throw new InterruptedIOException(); + } + checkCancelAt = count + BYTES_TO_WRITE_BEFORE_CANCEL_CHECK; + } + + int n = Math.min(len, BYTES_TO_WRITE_BEFORE_CANCEL_CHECK); + out.write(b, off, n); + md.update(b, off, n); + count += n; + + off += n; + len -= n; + } + } + + /** {@inheritDoc} */ + @Override + public void flush() throws IOException { + out.flush(); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java index 7104b9453e..2d0fe28dae 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java @@ -17,10 +17,8 @@ import static org.eclipse.jgit.lib.Constants.PACK_SIGNATURE; import java.io.IOException; import java.io.OutputStream; -import java.security.MessageDigest; -import org.eclipse.jgit.internal.JGitText; -import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.internal.storage.io.CancellableDigestOutputStream; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.util.NB; @@ -28,25 +26,14 @@ import org.eclipse.jgit.util.NB; * Custom output stream to support * {@link org.eclipse.jgit.internal.storage.pack.PackWriter}. */ -public final class PackOutputStream extends OutputStream { - private static final int BYTES_TO_WRITE_BEFORE_CANCEL_CHECK = 128 * 1024; - - private final ProgressMonitor writeMonitor; - - private final OutputStream out; +public final class PackOutputStream extends CancellableDigestOutputStream { private final PackWriter packWriter; - private final MessageDigest md = Constants.newMessageDigest(); - - private long count; - private final byte[] headerBuffer = new byte[32]; private final byte[] copyBuffer = new byte[64 << 10]; - private long checkCancelAt; - private boolean ofsDelta; /** @@ -66,48 +53,8 @@ public final class PackOutputStream extends OutputStream { */ public PackOutputStream(final ProgressMonitor writeMonitor, final OutputStream out, final PackWriter pw) { - this.writeMonitor = writeMonitor; - this.out = out; + super(writeMonitor, out); this.packWriter = pw; - this.checkCancelAt = BYTES_TO_WRITE_BEFORE_CANCEL_CHECK; - } - - /** {@inheritDoc} */ - @Override - public final void write(int b) throws IOException { - count++; - out.write(b); - md.update((byte) b); - } - - /** {@inheritDoc} */ - @Override - public final void write(byte[] b, int off, int len) - throws IOException { - while (0 < len) { - final int n = Math.min(len, BYTES_TO_WRITE_BEFORE_CANCEL_CHECK); - count += n; - - if (checkCancelAt <= count) { - if (writeMonitor.isCancelled()) { - throw new IOException( - JGitText.get().packingCancelledDuringObjectsWriting); - } - checkCancelAt = count + BYTES_TO_WRITE_BEFORE_CANCEL_CHECK; - } - - out.write(b, off, n); - md.update(b, off, n); - - off += n; - len -= n; - } - } - - /** {@inheritDoc} */ - @Override - public void flush() throws IOException { - out.flush(); } final void writeFileHeader(int version, long objectCount) @@ -160,7 +107,7 @@ public final class PackOutputStream extends OutputStream { ObjectToPack b = otp.getDeltaBase(); if (b != null && (b.isWritten() & ofsDelta)) { // Non-short-circuit logic is intentional int n = objectHeader(rawLength, OBJ_OFS_DELTA, headerBuffer); - n = ofsDelta(count - b.getOffset(), headerBuffer, n); + n = ofsDelta(length() - b.getOffset(), headerBuffer, n); write(headerBuffer, 0, n); } else if (otp.isDeltaRepresentation()) { int n = objectHeader(rawLength, OBJ_REF_DELTA, headerBuffer); @@ -209,20 +156,6 @@ public final class PackOutputStream extends OutputStream { } void endObject() { - writeMonitor.update(1); - } - - /** - * Get total number of bytes written since stream start. - * - * @return total number of bytes written since stream start. - */ - public final long length() { - return count; - } - - /** @return obtain the current SHA-1 digest. */ - final byte[] getDigest() { - return md.digest(); + getWriteMonitor().update(1); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java index 648d4a1821..659ccb8c55 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java @@ -502,6 +502,98 @@ public class OpenSshConfigFile implements SshConfigStore { } /** + * Converts an OpenSSH time value into a number of seconds. The format is + * defined by OpenSSH as a sequence of (positive) integers with suffixes for + * seconds, minutes, hours, days, and weeks. + * + * @param value + * to convert + * @return the parsed value as a number of seconds, or -1 if the value is + * not a valid OpenSSH time value + * @see <a href="https://man.openbsd.org/sshd_config.5#TIME_FORMATS">OpenBSD + * man 5 sshd_config, section TIME FORMATS</a> + */ + public static int timeSpec(String value) { + if (value == null) { + return -1; + } + try { + int length = value.length(); + int i = 0; + int seconds = 0; + boolean valueSeen = false; + while (i < length) { + // Skip whitespace + char ch = value.charAt(i); + if (Character.isWhitespace(ch)) { + i++; + continue; + } + if (ch == '+') { + // OpenSSH uses strtol with base 10: a leading plus sign is + // allowed. + i++; + } + int val = 0; + int j = i; + while (j < length) { + ch = value.charAt(j++); + if (ch >= '0' && ch <= '9') { + val = Math.addExact(Math.multiplyExact(val, 10), + ch - '0'); + } else { + j--; + break; + } + } + if (i == j) { + // No digits seen + return -1; + } + i = j; + int multiplier = 1; + if (i < length) { + ch = value.charAt(i++); + switch (ch) { + case 's': + case 'S': + break; + case 'm': + case 'M': + multiplier = 60; + break; + case 'h': + case 'H': + multiplier = 3600; + break; + case 'd': + case 'D': + multiplier = 24 * 3600; + break; + case 'w': + case 'W': + multiplier = 7 * 24 * 3600; + break; + default: + if (Character.isWhitespace(ch)) { + break; + } + // Invalid time spec + return -1; + } + } + seconds = Math.addExact(seconds, + Math.multiplyExact(val, multiplier)); + valueSeen = true; + } + return valueSeen ? seconds : -1; + } catch (ArithmeticException e) { + // Overflow + return -1; + } + } + + /** * Retrieves the local user name as given in the constructor. * * @return the user name @@ -549,6 +641,7 @@ public class OpenSshConfigFile implements SshConfigStore { LIST_KEYS.add(SshConstants.GLOBAL_KNOWN_HOSTS_FILE); LIST_KEYS.add(SshConstants.SEND_ENV); LIST_KEYS.add(SshConstants.USER_KNOWN_HOSTS_FILE); + LIST_KEYS.add(SshConstants.ADD_KEYS_TO_AGENT); // confirm timeSpec } /** @@ -871,7 +964,8 @@ public class OpenSshConfigFile implements SshConfigStore { if (options != null) { // HOSTNAME already done above String value = options.get(SshConstants.IDENTITY_AGENT); - if (value != null) { + if (value != null && !SshConstants.NONE.equals(value) + && !SshConstants.ENV_SSH_AUTH_SOCKET.equals(value)) { value = r.substitute(value, Replacer.DEFAULT_TOKENS, true); value = toFile(value, home).getPath(); options.put(SshConstants.IDENTITY_AGENT, value); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbrevConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbrevConfig.java new file mode 100644 index 0000000000..9109cfd769 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbrevConfig.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2022, Matthias Sohn <matthias.sohn@sap.com> and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.lib; + +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH; +import static org.eclipse.jgit.lib.TypedConfigGetter.UNSET_INT; + +import java.text.MessageFormat; + +import org.eclipse.jgit.api.errors.InvalidConfigurationException; +import org.eclipse.jgit.internal.JGitText; + +/** + * Git configuration option <a + * href=https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreabbrev"> + * core.abbrev</a> + * + * @since 6.1 + */ +public final class AbbrevConfig { + private static final String VALUE_NO = "no"; //$NON-NLS-1$ + + private static final String VALUE_AUTO = "auto"; //$NON-NLS-1$ + + /** + * The minimum value of abbrev + */ + public static final int MIN_ABBREV = 4; + + /** + * Cap configured core.abbrev to range between minimum of 4 and number of + * hex-digits of a full object id. + * + * @param len + * configured number of hex-digits to abbreviate object ids to + * @return core.abbrev capped to range between minimum of 4 and number of + * hex-digits of a full object id + */ + public static int capAbbrev(int len) { + return Math.min(Math.max(MIN_ABBREV, len), + Constants.OBJECT_ID_STRING_LENGTH); + } + + /** + * No abbreviation + */ + public final static AbbrevConfig NO = new AbbrevConfig( + Constants.OBJECT_ID_STRING_LENGTH); + + /** + * Parse string value of core.abbrev git option for a given repository + * + * @param repo + * repository + * @return the parsed AbbrevConfig + * @throws InvalidConfigurationException + * if value of core.abbrev is invalid + */ + public static AbbrevConfig parseFromConfig(Repository repo) + throws InvalidConfigurationException { + Config config = repo.getConfig(); + String value = config.getString(ConfigConstants.CONFIG_CORE_SECTION, + null, ConfigConstants.CONFIG_KEY_ABBREV); + if (value == null || value.equalsIgnoreCase(VALUE_AUTO)) { + return auto(repo); + } + if (value.equalsIgnoreCase(VALUE_NO)) { + return NO; + } + try { + int len = config.getIntInRange(ConfigConstants.CONFIG_CORE_SECTION, + ConfigConstants.CONFIG_KEY_ABBREV, MIN_ABBREV, + Constants.OBJECT_ID_STRING_LENGTH, UNSET_INT); + if (len == UNSET_INT) { + // Unset was checked above. If we get UNSET_INT here, then + // either the value was UNSET_INT, or it was an invalid value + // (not an integer, or out of range), and EGit's + // ReportingTypedGetter caught the exception and has logged a + // warning. In either case we should fall back to some sane + // default. + len = OBJECT_ID_ABBREV_STRING_LENGTH; + } + return new AbbrevConfig(len); + } catch (IllegalArgumentException e) { + throw new InvalidConfigurationException(MessageFormat + .format(JGitText.get().invalidCoreAbbrev, value), e); + } + } + + /** + * An appropriate value is computed based on the approximate number of + * packed objects in a repository, which hopefully is enough for abbreviated + * object names to stay unique for some time. + * + * @param repo + * @return appropriate value computed based on the approximate number of + * packed objects in a repository + */ + private static AbbrevConfig auto(Repository repo) { + long count = repo.getObjectDatabase().getApproximateObjectCount(); + if (count == -1) { + return new AbbrevConfig(OBJECT_ID_ABBREV_STRING_LENGTH); + } + // find msb, round to next power of 2 + int len = 63 - Long.numberOfLeadingZeros(count) + 1; + // With the order of 2^len objects, we expect a collision at + // 2^(len/2). But we also care about hex chars, not bits, and + // there are 4 bits per hex. So all together we need to divide + // by 2; but we also want to round odd numbers up, hence adding + // one before dividing. + len = (len + 1) / 2; + // for small repos use at least fallback length + return new AbbrevConfig(Math.max(len, OBJECT_ID_ABBREV_STRING_LENGTH)); + } + + /** + * All other possible abbreviation lengths. Valid range 4 to number of + * hex-digits of an unabbreviated object id (40 for SHA1 object ids, jgit + * doesn't support SHA256 yet). + */ + private int abbrev; + + /** + * @param abbrev + */ + private AbbrevConfig(int abbrev) { + this.abbrev = capAbbrev(abbrev); + } + + /** + * Get the configured abbreviation length for object ids. + * + * @return the configured abbreviation length for object ids + */ + public int get() { + return abbrev; + } + + @Override + public String toString() { + return Integer.toString(abbrev); + } +}
\ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java index 6da6f1204a..aa613d07eb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java @@ -138,6 +138,18 @@ public class BranchConfig { } /** + * Get the remote this branch is configured to push to. + * + * @return the remote this branch is configured to push to, or {@code null} + * if not defined + * @since 6.1 + */ + public String getPushRemote() { + return config.getString(ConfigConstants.CONFIG_BRANCH_SECTION, + branchName, ConfigConstants.CONFIG_KEY_PUSH_REMOTE); + } + + /** * Get the name of the upstream branch as it is called on the remote * * @return the name of the upstream branch as it is called on the remote, or 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 22e1f98181..55cc02683a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java @@ -18,11 +18,13 @@ import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import java.text.MessageFormat; +import java.util.Locale; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.Config.ConfigEnum; import org.eclipse.jgit.lib.Config.SectionParser; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.IO; @@ -34,22 +36,76 @@ import org.eclipse.jgit.util.RawParseUtils; * @since 5.13 */ public class CommitConfig { + /** * Key for {@link Config#get(SectionParser)}. */ public static final Config.SectionParser<CommitConfig> KEY = CommitConfig::new; + private static final String CUT = " ------------------------ >8 ------------------------\n"; //$NON-NLS-1$ + + /** + * How to clean up commit messages when committing. + * + * @since 6.1 + */ + public enum CleanupMode implements ConfigEnum { + + /** + * {@link #WHITESPACE}, additionally remove comment lines. + */ + STRIP, + + /** + * Remove trailing whitespace and leading and trailing empty lines; + * collapse multiple empty lines to a single one. + */ + WHITESPACE, + + /** + * Make no changes. + */ + VERBATIM, + + /** + * Omit everything from the first "scissor" line on, then apply + * {@link #WHITESPACE}. + */ + SCISSORS, + + /** + * Use {@link #STRIP} for user-edited messages, otherwise + * {@link #WHITESPACE}, unless overridden by a git config setting other + * than DEFAULT. + */ + DEFAULT; + + @Override + public String toConfigValue() { + return name().toLowerCase(Locale.ROOT); + } + + @Override + public boolean matchConfigValue(String in) { + return toConfigValue().equals(in); + } + } + private final static Charset DEFAULT_COMMIT_MESSAGE_ENCODING = StandardCharsets.UTF_8; private String i18nCommitEncoding; private String commitTemplatePath; + private CleanupMode cleanupMode; + private CommitConfig(Config rc) { commitTemplatePath = rc.getString(ConfigConstants.CONFIG_COMMIT_SECTION, null, ConfigConstants.CONFIG_KEY_COMMIT_TEMPLATE); i18nCommitEncoding = rc.getString(ConfigConstants.CONFIG_SECTION_I18N, null, ConfigConstants.CONFIG_KEY_COMMIT_ENCODING); + cleanupMode = rc.getEnum(ConfigConstants.CONFIG_COMMIT_SECTION, null, + ConfigConstants.CONFIG_KEY_CLEANUP, CleanupMode.DEFAULT); } /** @@ -75,6 +131,48 @@ public class CommitConfig { } /** + * Retrieves the {@link CleanupMode} as given by git config + * {@code commit.cleanup}. + * + * @return the {@link CleanupMode}; {@link CleanupMode#DEFAULT} if the git + * config is not set + * @since 6.1 + */ + @NonNull + public CleanupMode getCleanupMode() { + return cleanupMode; + } + + /** + * Computes a non-default {@link CleanupMode} from the given mode and the + * git config. + * + * @param mode + * {@link CleanupMode} to resolve + * @param defaultStrip + * if {@code true} return {@link CleanupMode#STRIP} if the git + * config is also "default", otherwise return + * {@link CleanupMode#WHITESPACE} + * @return the {@code mode}, if it is not {@link CleanupMode#DEFAULT}, + * otherwise the resolved mode, which is never + * {@link CleanupMode#DEFAULT} + * @since 6.1 + */ + @NonNull + public CleanupMode resolve(@NonNull CleanupMode mode, + boolean defaultStrip) { + if (CleanupMode.DEFAULT == mode) { + CleanupMode defaultMode = getCleanupMode(); + if (CleanupMode.DEFAULT == defaultMode) { + return defaultStrip ? CleanupMode.STRIP + : CleanupMode.WHITESPACE; + } + return defaultMode; + } + return mode; + } + + /** * Get the content to the commit template as defined in * {@code commit.template}. If no {@code i18n.commitEncoding} is specified, * UTF-8 fallback is used. @@ -135,4 +233,86 @@ public class CommitConfig { return commitMessageEncoding; } + + /** + * Processes a text according to the given {@link CleanupMode}. + * + * @param text + * text to process + * @param mode + * {@link CleanupMode} to use + * @param commentChar + * comment character (normally {@code #}) to use if {@code mode} + * is {@link CleanupMode#STRIP} or {@link CleanupMode#SCISSORS} + * @return the processed text + * @throws IllegalArgumentException + * if {@code mode} is {@link CleanupMode#DEFAULT} (use + * {@link #resolve(CleanupMode, boolean)} first) + * @since 6.1 + */ + public static String cleanText(@NonNull String text, + @NonNull CleanupMode mode, char commentChar) { + String toProcess = text; + boolean strip = false; + switch (mode) { + case VERBATIM: + return text; + case SCISSORS: + String cut = commentChar + CUT; + if (text.startsWith(cut)) { + return ""; //$NON-NLS-1$ + } + int cutPos = text.indexOf('\n' + cut); + if (cutPos >= 0) { + toProcess = text.substring(0, cutPos + 1); + } + break; + case STRIP: + strip = true; + break; + case WHITESPACE: + break; + case DEFAULT: + default: + // Internal error; no translation + throw new IllegalArgumentException("Invalid clean-up mode " + mode); //$NON-NLS-1$ + } + // WHITESPACE + StringBuilder result = new StringBuilder(); + boolean lastWasEmpty = true; + for (String line : toProcess.split("\n")) { //$NON-NLS-1$ + line = line.stripTrailing(); + if (line.isEmpty()) { + if (!lastWasEmpty) { + result.append('\n'); + lastWasEmpty = true; + } + } else if (!strip || !isComment(line, commentChar)) { + lastWasEmpty = false; + result.append(line).append('\n'); + } + } + int bufferSize = result.length(); + if (lastWasEmpty && bufferSize > 0) { + bufferSize--; + result.setLength(bufferSize); + } + if (bufferSize > 0 && !toProcess.endsWith("\n")) { //$NON-NLS-1$ + if (result.charAt(bufferSize - 1) == '\n') { + result.setLength(bufferSize - 1); + } + } + return result.toString(); + } + + private static boolean isComment(String text, char commentChar) { + int len = text.length(); + for (int i = 0; i < len; i++) { + char ch = text.charAt(i); + if (!Character.isWhitespace(ch)) { + return ch == commentChar; + } + } + return false; + } }
\ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java index 1ce3e312e2..d1d66d280e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java @@ -278,6 +278,54 @@ public class Config { } /** + * Obtain an integer value from the configuration which must be inside given + * range. + * + * @param section + * section the key is grouped within. + * @param name + * name of the key to get. + * @param minValue + * minimum value + * @param maxValue + * maximum value + * @param defaultValue + * default value to return if no value was present. + * @return an integer value from the configuration, or defaultValue. + * @since 6.1 + */ + public int getIntInRange(String section, String name, int minValue, + int maxValue, int defaultValue) { + return typedGetter.getIntInRange(this, section, null, name, minValue, + maxValue, defaultValue); + } + + /** + * Obtain an integer value from the configuration which must be inside given + * range. + * + * @param section + * section the key is grouped within. + * @param subsection + * subsection name, such a remote or branch name. + * @param name + * name of the key to get. + * @param minValue + * minimum value + * @param maxValue + * maximum value + * @param defaultValue + * default value to return if no value was present. + * @return an integer value from the configuration, or defaultValue. + * @since 6.1 + */ + public int getIntInRange(String section, String subsection, String name, + int minValue, int maxValue, int defaultValue) { + return typedGetter.getIntInRange(this, section, subsection, name, + minValue, maxValue, defaultValue); + } + + /** * Obtain an integer value from the configuration. * * @param section 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 605b44bee8..2d8354947d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java @@ -1,7 +1,8 @@ /* * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com> * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> - * Copyright (C) 2012, 2020, Robin Rosenberg and others + * Copyright (C) 2012-2013, Robin Rosenberg + * Copyright (C) 2018-2021, 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 @@ -29,6 +30,48 @@ public final class ConfigConstants { /** The "diff" section */ public static final String CONFIG_DIFF_SECTION = "diff"; + /** + * The "tool" key within "diff" section + * + * @since 6.1 + */ + public static final String CONFIG_KEY_TOOL = "tool"; + + /** + * The "guitool" key within "diff" section + * + * @since 6.1 + */ + public static final String CONFIG_KEY_GUITOOL = "guitool"; + + /** + * The "difftool" section + * + * @since 6.1 + */ + public static final String CONFIG_DIFFTOOL_SECTION = "difftool"; + + /** + * The "prompt" key within "difftool" section + * + * @since 6.1 + */ + public static final String CONFIG_KEY_PROMPT = "prompt"; + + /** + * The "trustExitCode" key within "difftool" section + * + * @since 6.1 + */ + public static final String CONFIG_KEY_TRUST_EXIT_CODE = "trustExitCode"; + + /** + * The "cmd" key within "difftool.*." section + * + * @since 6.1 + */ + public static final String CONFIG_KEY_CMD = "cmd"; + /** The "dfs" section */ public static final String CONFIG_DFS_SECTION = "dfs"; @@ -145,6 +188,13 @@ public final class ConfigConstants { public static final String CONFIG_TAG_SECTION = "tag"; /** + * The "cleanup" key + * + * @since 6.1 + */ + public static final String CONFIG_KEY_CLEANUP = "cleanup"; + + /** * The "gpgSign" key * * @since 5.2 @@ -285,6 +335,20 @@ public final class ConfigConstants { /** The "remote" key */ public static final String CONFIG_KEY_REMOTE = "remote"; + /** + * The "pushRemote" key. + * + * @since 6.1 + */ + public static final String CONFIG_KEY_PUSH_REMOTE = "pushRemote"; + + /** + * The "pushDefault" key. + * + * @since 6.1 + */ + public static final String CONFIG_KEY_PUSH_DEFAULT = "pushDefault"; + /** The "merge" key */ public static final String CONFIG_KEY_MERGE = "merge"; @@ -791,6 +855,34 @@ public final class ConfigConstants { public static final String CONFIG_KEY_SEARCH_FOR_REUSE_TIMEOUT = "searchforreusetimeout"; /** + * The "push" section. + * + * @since 6.1 + */ + public static final String CONFIG_PUSH_SECTION = "push"; + + /** + * The "default" key. + * + * @since 6.1 + */ + public static final String CONFIG_KEY_DEFAULT = "default"; + + /** + * The "abbrev" key + * + * @since 6.1 + */ + public static final String CONFIG_KEY_ABBREV = "abbrev"; + + /** + * The "trustPackedRefsStat" key + * + * @since 6.1.1 + */ + public static final String CONFIG_KEY_TRUST_PACKED_REFS_STAT = "trustPackedRefsStat"; + + /** * The "pack.preserveOldPacks" key * * @since 5.13.2 @@ -803,5 +895,4 @@ public final class ConfigConstants { * @since 5.13.2 */ public static final String CONFIG_KEY_PRUNE_PRESERVED = "prunepreserved"; - } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java index 92367ebd0c..cf2e69dbb5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -48,6 +48,15 @@ public final class Constants { */ public static final int OBJECT_ID_STRING_LENGTH = OBJECT_ID_LENGTH * 2; + /** + * The historic length of an abbreviated Git object hash string. Git 2.11 + * changed this static number to a dynamically calculated one that scales + * as the repository grows. + * + * @since 6.1 + */ + public static final int OBJECT_ID_ABBREV_STRING_LENGTH = 7; + /** Special name for the "HEAD" symbolic-ref. */ public static final String HEAD = "HEAD"; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java index f23c6e08d1..fc82a5fead 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java @@ -116,6 +116,27 @@ public class CoreConfig { ALWAYS } + /** + * Permissible values for {@code core.trustPackedRefsStat}. + * + * @since 6.1.1 + */ + public enum TrustPackedRefsStat { + /** Do not trust file attributes of the packed-refs file. */ + NEVER, + + /** Trust file attributes of the packed-refs file. */ + ALWAYS, + + /** Open and close the packed-refs file to refresh its file attributes + * and then trust it. */ + AFTER_OPEN, + + /** {@code core.trustPackedRefsStat} defaults to this when it is + * not set */ + UNSET + } + private final int compression; private final int packIndexVersion; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java index 9f96bce251..86409403b0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java @@ -120,6 +120,26 @@ public class DefaultTypedConfigGetter implements TypedConfigGetter { /** {@inheritDoc} */ @Override + public int getIntInRange(Config config, String section, String subsection, + String name, int minValue, int maxValue, int defaultValue) { + int val = getInt(config, section, subsection, name, defaultValue); + if ((val >= minValue && val <= maxValue) || val == UNSET_INT) { + return val; + } + if (subsection == null) { + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().integerValueNotInRange, section, name, + Integer.valueOf(val), Integer.valueOf(minValue), + Integer.valueOf(maxValue))); + } + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().integerValueNotInRangeSubSection, section, + subsection, name, Integer.valueOf(val), + Integer.valueOf(minValue), Integer.valueOf(maxValue))); + } + + /** {@inheritDoc} */ + @Override public long getLong(Config config, String section, String subsection, String name, long defaultValue) { final String str = config.getString(section, subsection, name); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java index 28ea927b14..df9fd47efa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java @@ -568,6 +568,9 @@ public class IndexDiff { if (ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL) { try (SubmoduleWalk smw = new SubmoduleWalk(repository)) { smw.setTree(new DirCacheIterator(dirCache)); + if (filter != null) { + smw.setFilter(filter); + } smw.setBuilderFactory(factory); while (smw.next()) { IgnoreSubmoduleMode localIgnoreSubmoduleMode = ignoreSubmoduleMode; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java index 04262c07ae..70009cba35 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java @@ -155,4 +155,14 @@ public abstract class ObjectDatabase implements AutoCloseable { public ObjectDatabase newCachedDatabase() { return this; } + + /** + * Get a quick, rough count of objects in this repository. Ignores loose + * objects. Returns {@code -1} if an exception occurs. + * + * @return quick, rough count of objects in this repository, {@code -1} if + * an exception occurs + * @since 6.1 + */ + public abstract long getApproximateObjectCount(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java index a2c7381ce7..26c3ff6718 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java @@ -10,6 +10,8 @@ package org.eclipse.jgit.lib; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH; + import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -76,7 +78,7 @@ public abstract class ObjectReader implements AutoCloseable { */ public AbbreviatedObjectId abbreviate(AnyObjectId objectId) throws IOException { - return abbreviate(objectId, 7); + return abbreviate(objectId, OBJECT_ID_ABBREV_STRING_LENGTH); } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java index 428a6b959c..93710299b4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java @@ -14,6 +14,8 @@ package org.eclipse.jgit.lib; import java.io.Serializable; import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.ZoneId; import java.util.Date; import java.util.Locale; import java.util.TimeZone; @@ -206,6 +208,20 @@ public class PersonIdent implements Serializable { } /** + * Copy a {@link org.eclipse.jgit.lib.PersonIdent}, but alter the clone's + * time stamp + * + * @param pi + * original {@link org.eclipse.jgit.lib.PersonIdent} + * @param aWhen + * local time as Instant + * @since 6.1 + */ + public PersonIdent(PersonIdent pi, Instant aWhen) { + this(pi.getName(), pi.getEmailAddress(), aWhen.toEpochMilli(), pi.tzOffset); + } + + /** * Construct a PersonIdent from simple data * * @param aName a {@link java.lang.String} object. @@ -222,6 +238,27 @@ public class PersonIdent implements Serializable { } /** + * Construct a PersonIdent from simple data + * + * @param aName + * a {@link java.lang.String} object. + * @param aEmailAddress + * a {@link java.lang.String} object. + * @param aWhen + * local time stamp + * @param zoneId + * time zone id + * @since 6.1 + */ + public PersonIdent(final String aName, String aEmailAddress, Instant aWhen, + ZoneId zoneId) { + this(aName, aEmailAddress, aWhen.toEpochMilli(), + TimeZone.getTimeZone(zoneId) + .getOffset(aWhen + .toEpochMilli()) / (60 * 1000)); + } + + /** * Copy a PersonIdent, but alter the clone's time stamp * * @param pi @@ -304,6 +341,16 @@ public class PersonIdent implements Serializable { } /** + * Get when attribute as instant + * + * @return timestamp + * @since 6.1 + */ + public Instant getWhenAsInstant() { + return Instant.ofEpochMilli(when); + } + + /** * Get this person's declared time zone * * @return this person's declared time zone; null if time zone is unknown. @@ -313,6 +360,16 @@ public class PersonIdent implements Serializable { } /** + * Get the time zone id + * + * @return the time zone id + * @since 6.1 + */ + public ZoneId getZoneId() { + return getTimeZone().toZoneId(); + } + + /** * Get this person's declared time zone as minutes east of UTC. * * @return this person's declared time zone as minutes east of UTC. If the diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java index 0f2f6cff8a..c4eb8f10d5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java @@ -29,6 +29,13 @@ import org.eclipse.jgit.util.FS; public interface TypedConfigGetter { /** + * Use {@code Integer#MIN_VALUE} as unset int value + * + * @since 6.1 + */ + public static final int UNSET_INT = Integer.MIN_VALUE; + + /** * Get a boolean value from a git {@link Config}. * * @param config @@ -87,6 +94,32 @@ public interface TypedConfigGetter { int defaultValue); /** + * Obtain an integer value from a git {@link Config} which must be in given + * range. + * + * @param config + * to get the value from + * @param section + * section the key is grouped within. + * @param subsection + * subsection name, such a remote or branch name. + * @param name + * name of the key to get. + * @param minValue + * minimal value + * @param maxValue + * maximum value + * @param defaultValue + * default value to return if no value was present. Use + * {@code #UNSET_INT} to set the default to unset. + * @return an integer value from the configuration, or defaultValue. + * {@code #UNSET_INT} if unset. + * @since 6.1 + */ + int getIntInRange(Config config, String section, String subsection, + String name, int minValue, int maxValue, int defaultValue); + + /** * Obtain a long value from a git {@link Config}. * * @param config diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BooleanTriState.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BooleanTriState.java new file mode 100644 index 0000000000..44d3bb36e0 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BooleanTriState.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 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.lib.internal; + +/** + * A boolean value that can also have an unset state. + */ +public enum BooleanTriState { + /** + * Value equivalent to {@code true}. + */ + TRUE, + /** + * Value equivalent to {@code false}. + */ + FALSE, + /** + * Value is not set. + */ + UNSET; +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java index f7966a267f..e0c083f55c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java @@ -92,36 +92,62 @@ public class MergeMessageFormatter { } /** - * Add section with conflicting paths to merge message. + * Add section with conflicting paths to merge message. Lines are prefixed + * with a hash. * * @param message * the original merge message * @param conflictingPaths * the paths with conflicts * @return merge message with conflicting paths added + * @deprecated since 6.1; use + * {@link #formatWithConflicts(String, Iterable, char)} instead */ + @Deprecated public String formatWithConflicts(String message, List<String> conflictingPaths) { + return formatWithConflicts(message, conflictingPaths, '#'); + } + + /** + * Add section with conflicting paths to merge message. + * + * @param message + * the original merge message + * @param conflictingPaths + * the paths with conflicts + * @param commentChar + * comment character to use for prefixing the conflict lines + * @return merge message with conflicting paths added + * @since 6.1 + */ + public String formatWithConflicts(String message, + Iterable<String> conflictingPaths, char commentChar) { StringBuilder sb = new StringBuilder(); String[] lines = message.split("\n"); //$NON-NLS-1$ int firstFooterLine = ChangeIdUtil.indexOfFirstFooterLine(lines); - for (int i = 0; i < firstFooterLine; i++) + for (int i = 0; i < firstFooterLine; i++) { sb.append(lines[i]).append('\n'); - if (firstFooterLine == lines.length && message.length() != 0) + } + if (firstFooterLine == lines.length && message.length() != 0) { sb.append('\n'); - addConflictsMessage(conflictingPaths, sb); - if (firstFooterLine < lines.length) + } + addConflictsMessage(conflictingPaths, sb, commentChar); + if (firstFooterLine < lines.length) { sb.append('\n'); - for (int i = firstFooterLine; i < lines.length; i++) + } + for (int i = firstFooterLine; i < lines.length; i++) { sb.append(lines[i]).append('\n'); + } return sb.toString(); } - private static void addConflictsMessage(List<String> conflictingPaths, - StringBuilder sb) { - sb.append("Conflicts:\n"); //$NON-NLS-1$ + private static void addConflictsMessage(Iterable<String> conflictingPaths, + StringBuilder sb, char commentChar) { + sb.append(commentChar).append(" Conflicts:\n"); //$NON-NLS-1$ for (String conflictingPath : conflictingPaths) { - sb.append('\t').append(conflictingPath).append('\n'); + sb.append(commentChar).append('\t').append(conflictingPath) + .append('\n'); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java index 7767662867..b9ab1d1b7a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -3,7 +3,7 @@ * Copyright (C) 2010-2012, Matthias Sohn <matthias.sohn@sap.com> * Copyright (C) 2012, Research In Motion Limited * Copyright (C) 2017, Obeo (mathieu.cartaud@obeo.fr) - * 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 @@ -276,11 +276,15 @@ public class ResolveMerger extends ThreeWayMerger { private ContentMergeStrategy contentStrategy = ContentMergeStrategy.CONFLICT; /** - * Keeps {@link CheckoutMetadata} for {@link #checkout()} and - * {@link #cleanUp()}. + * Keeps {@link CheckoutMetadata} for {@link #checkout()}. */ private Map<String, CheckoutMetadata> checkoutMetadata; + /** + * Keeps {@link CheckoutMetadata} for {@link #cleanUp()}. + */ + private Map<String, CheckoutMetadata> cleanupMetadata; + private static MergeAlgorithm getMergeAlgorithm(Config config) { SupportedAlgorithm diffAlg = config.getEnum( CONFIG_DIFF_SECTION, null, CONFIG_KEY_ALGORITHM, @@ -383,12 +387,14 @@ public class ResolveMerger extends ThreeWayMerger { } if (!inCore) { checkoutMetadata = new HashMap<>(); + cleanupMetadata = new HashMap<>(); } try { return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1], false); } finally { checkoutMetadata = null; + cleanupMetadata = null; if (implicitDirCache) { dircache.unlock(); } @@ -447,7 +453,7 @@ public class ResolveMerger extends ThreeWayMerger { DirCacheEntry entry = dc.getEntry(mpath); if (entry != null) { DirCacheCheckout.checkoutEntry(db, entry, reader, false, - checkoutMetadata.get(mpath)); + cleanupMetadata.get(mpath)); } mpathsIt.remove(); } @@ -501,22 +507,26 @@ public class ResolveMerger extends ThreeWayMerger { * Remembers the {@link CheckoutMetadata} for the given path; it may be * needed in {@link #checkout()} or in {@link #cleanUp()}. * + * @param map + * to add the metadata to * @param path * of the current node * @param attributes - * for the current node + * to use for determining the metadata * @throws IOException * if the smudge filter cannot be determined - * @since 5.1 + * @since 6.1 */ - protected void addCheckoutMetadata(String path, Attributes attributes) + protected void addCheckoutMetadata(Map<String, CheckoutMetadata> map, + String path, Attributes attributes) throws IOException { - if (checkoutMetadata != null) { + if (map != null) { EolStreamType eol = EolStreamTypeUtil.detectStreamType( - OperationType.CHECKOUT_OP, workingTreeOptions, attributes); + OperationType.CHECKOUT_OP, workingTreeOptions, + attributes); CheckoutMetadata data = new CheckoutMetadata(eol, - tw.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE)); - checkoutMetadata.put(path, data); + tw.getSmudgeCommand(attributes)); + map.put(path, data); } } @@ -529,15 +539,17 @@ public class ResolveMerger extends ThreeWayMerger { * @param entry * to add * @param attributes - * for the current entry + * the {@link Attributes} of the trees * @throws IOException * if the {@link CheckoutMetadata} cannot be determined - * @since 5.1 + * @since 6.1 */ protected void addToCheckout(String path, DirCacheEntry entry, - Attributes attributes) throws IOException { + Attributes[] attributes) + throws IOException { toBeCheckedOut.put(path, entry); - addCheckoutMetadata(path, attributes); + addCheckoutMetadata(cleanupMetadata, path, attributes[T_OURS]); + addCheckoutMetadata(checkoutMetadata, path, attributes[T_THEIRS]); } /** @@ -549,7 +561,7 @@ public class ResolveMerger extends ThreeWayMerger { * @param isFile * whether it is a file * @param attributes - * for the entry + * to use for determining the {@link CheckoutMetadata} * @throws IOException * if the {@link CheckoutMetadata} cannot be determined * @since 5.1 @@ -558,7 +570,7 @@ public class ResolveMerger extends ThreeWayMerger { Attributes attributes) throws IOException { toBeDeleted.add(path); if (isFile) { - addCheckoutMetadata(path, attributes); + addCheckoutMetadata(cleanupMetadata, path, attributes); } } @@ -599,7 +611,7 @@ public class ResolveMerger extends ThreeWayMerger { * see * {@link org.eclipse.jgit.merge.ResolveMerger#mergeTrees(AbstractTreeIterator, RevTree, RevTree, boolean)} * @param attributes - * the attributes defined for this entry + * the {@link Attributes} for the three trees * @return <code>false</code> if the merge will fail because the index entry * didn't match ours or the working-dir file was dirty and a * conflict occurred @@ -607,12 +619,12 @@ public class ResolveMerger extends ThreeWayMerger { * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException * @throws org.eclipse.jgit.errors.CorruptObjectException * @throws java.io.IOException - * @since 4.9 + * @since 6.1 */ protected boolean processEntry(CanonicalTreeParser base, CanonicalTreeParser ours, CanonicalTreeParser theirs, DirCacheBuildIterator index, WorkingTreeIterator work, - boolean ignoreConflicts, Attributes attributes) + boolean ignoreConflicts, Attributes[] attributes) throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException { enterSubtree = true; @@ -729,7 +741,7 @@ public class ResolveMerger extends ThreeWayMerger { // Base, ours, and theirs all contain a folder: don't delete return true; } - addDeletion(tw.getPathString(), nonTree(modeO), attributes); + addDeletion(tw.getPathString(), nonTree(modeO), attributes[T_OURS]); return true; } @@ -772,7 +784,7 @@ public class ResolveMerger extends ThreeWayMerger { if (nonTree(modeO) && nonTree(modeT)) { // Check worktree before modifying files boolean worktreeDirty = isWorktreeDirty(work, ourDce); - if (!attributes.canBeContentMerged() && worktreeDirty) { + if (!attributes[T_OURS].canBeContentMerged() && worktreeDirty) { return false; } @@ -791,7 +803,7 @@ public class ResolveMerger extends ThreeWayMerger { mergeResults.put(tw.getPathString(), result); unmergedPaths.add(tw.getPathString()); return true; - } else if (!attributes.canBeContentMerged()) { + } else if (!attributes[T_OURS].canBeContentMerged()) { // File marked as binary switch (getContentMergeStrategy()) { case OURS: @@ -842,13 +854,16 @@ public class ResolveMerger extends ThreeWayMerger { if (ignoreConflicts) { result.setContainsConflicts(false); } - updateIndex(base, ours, theirs, result, attributes); + updateIndex(base, ours, theirs, result, attributes[T_OURS]); String currentPath = tw.getPathString(); if (result.containsConflicts() && !ignoreConflicts) { unmergedPaths.add(currentPath); } modifiedFiles.add(currentPath); - addCheckoutMetadata(currentPath, attributes); + addCheckoutMetadata(cleanupMetadata, currentPath, + attributes[T_OURS]); + addCheckoutMetadata(checkoutMetadata, currentPath, + attributes[T_THEIRS]); } else if (modeO != modeT) { // OURS or THEIRS has been deleted if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw @@ -881,7 +896,8 @@ public class ResolveMerger extends ThreeWayMerger { // markers). But also stage 0 of the index is filled // with that content. result.setContainsConflicts(false); - updateIndex(base, ours, theirs, result, attributes); + updateIndex(base, ours, theirs, result, + attributes[T_OURS]); } else { add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0); @@ -896,11 +912,9 @@ public class ResolveMerger extends ThreeWayMerger { if (isWorktreeDirty(work, ourDce)) { return false; } - if (nonTree(modeT)) { - if (e != null) { - addToCheckout(tw.getPathString(), e, - attributes); - } + if (nonTree(modeT) && e != null) { + addToCheckout(tw.getPathString(), e, + attributes); } } @@ -945,14 +959,16 @@ public class ResolveMerger extends ThreeWayMerger { */ private MergeResult<RawText> contentMerge(CanonicalTreeParser base, CanonicalTreeParser ours, CanonicalTreeParser theirs, - Attributes attributes, ContentMergeStrategy strategy) + Attributes[] attributes, ContentMergeStrategy strategy) throws BinaryBlobException, IOException { + // TW: The attributes here are used to determine the LFS smudge filter. + // Is doing a content merge on LFS items really a good idea?? RawText baseText = base == null ? RawText.EMPTY_TEXT - : getRawText(base.getEntryObjectId(), attributes); + : getRawText(base.getEntryObjectId(), attributes[T_BASE]); RawText ourText = ours == null ? RawText.EMPTY_TEXT - : getRawText(ours.getEntryObjectId(), attributes); + : getRawText(ours.getEntryObjectId(), attributes[T_OURS]); RawText theirsText = theirs == null ? RawText.EMPTY_TEXT - : getRawText(theirs.getEntryObjectId(), attributes); + : getRawText(theirs.getEntryObjectId(), attributes[T_THEIRS]); mergeAlgorithm.setContentMergeStrategy(strategy); return mergeAlgorithm.merge(RawTextComparator.DEFAULT, baseText, ourText, theirsText); @@ -1342,7 +1358,7 @@ public class ResolveMerger extends ThreeWayMerger { tw = new NameConflictTreeWalk(db, reader); tw.addTree(baseTree); - tw.addTree(headTree); + tw.setHead(tw.addTree(headTree)); tw.addTree(mergeTree); int dciPos = tw.addTree(buildIt); if (workingTreeIterator != null) { @@ -1403,6 +1419,13 @@ public class ResolveMerger extends ThreeWayMerger { boolean hasAttributeNodeProvider = treeWalk .getAttributesNodeProvider() != null; while (treeWalk.next()) { + Attributes[] attributes = { NO_ATTRIBUTES, NO_ATTRIBUTES, + NO_ATTRIBUTES }; + if (hasAttributeNodeProvider) { + attributes[T_BASE] = treeWalk.getAttributes(T_BASE); + attributes[T_OURS] = treeWalk.getAttributes(T_OURS); + attributes[T_THEIRS] = treeWalk.getAttributes(T_THEIRS); + } if (!processEntry( treeWalk.getTree(T_BASE, CanonicalTreeParser.class), treeWalk.getTree(T_OURS, CanonicalTreeParser.class), @@ -1410,9 +1433,7 @@ public class ResolveMerger extends ThreeWayMerger { treeWalk.getTree(T_INDEX, DirCacheBuildIterator.class), hasWorkingTreeIterator ? treeWalk.getTree(T_FILE, WorkingTreeIterator.class) : null, - ignoreConflicts, hasAttributeNodeProvider - ? treeWalk.getAttributes() - : NO_ATTRIBUTES)) { + ignoreConflicts, attributes)) { cleanUp(); return false; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java index e6f9580bf7..4e48a5c328 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java @@ -1,5 +1,5 @@ /* - * 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 @@ -139,7 +139,7 @@ public class ObjectWalk extends RevWalk { * the repository the walker will obtain data from. */ public ObjectWalk(Repository repo) { - this(repo.newObjectReader()); + this(repo.newObjectReader(), true); } /** @@ -151,7 +151,11 @@ public class ObjectWalk extends RevWalk { * required. */ public ObjectWalk(ObjectReader or) { - super(or); + this(or, false); + } + + private ObjectWalk(ObjectReader or, boolean closeReader) { + super(or, closeReader); setRetainBody(false); rootObjects = new ArrayList<>(); pendingObjects = new BlockObjQueue(); 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 8d571f5b14..a50eaf1a8a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java @@ -215,7 +215,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { this(or, false); } - private RevWalk(ObjectReader or, boolean closeReader) { + RevWalk(ObjectReader or, boolean closeReader) { reader = or; idBuffer = new MutableObjectId(); objects = new ObjectIdOwnerMap<>(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java index 81a70af2d2..768b9984af 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java @@ -750,8 +750,19 @@ public class AmazonS3 { final XMLReader xr; try { - xr = SAXParserFactory.newInstance().newSAXParser() - .getXMLReader(); + SAXParserFactory saxParserFactory = SAXParserFactory + .newInstance(); + saxParserFactory.setNamespaceAware(true); + saxParserFactory.setFeature( + "http://xml.org/sax/features/external-general-entities", //$NON-NLS-1$ + false); + saxParserFactory.setFeature( + "http://xml.org/sax/features/external-parameter-entities", //$NON-NLS-1$ + false); + saxParserFactory.setFeature( + "http://apache.org/xml/features/disallow-doctype-decl", //$NON-NLS-1$ + true); + xr = saxParserFactory.newSAXParser().getXMLReader(); } catch (SAXException | ParserConfigurationException e) { throw new IOException( JGitText.get().noXMLParserAvailable, e); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConfig.java index fda7a8152a..c8774d546a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017, David Pursehouse <david.pursehouse@gmail.com> and others + * Copyright (C) 2017, 2022 David Pursehouse <david.pursehouse@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 @@ -10,7 +10,10 @@ package org.eclipse.jgit.transport; +import java.util.Locale; + import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.util.StringUtils; /** @@ -19,8 +22,9 @@ import org.eclipse.jgit.util.StringUtils; * @since 4.9 */ public class PushConfig { + /** - * Config values for push.recurseSubmodules. + * Git config values for {@code push.recurseSubmodules}. */ public enum PushRecurseSubmodulesMode implements Config.ConfigEnum { /** @@ -59,4 +63,100 @@ public class PushConfig { || configValue.equalsIgnoreCase(s); } } + + /** + * Git config values for {@code push.default}. + * + * @since 6.1 + */ + public enum PushDefault implements Config.ConfigEnum { + + /** + * Do not push if there are no explicit refspecs. + */ + NOTHING, + + /** + * Push the current branch to an upstream branch of the same name. + */ + CURRENT, + + /** + * Push the current branch to an upstream branch determined by git + * config {@code branch.<currentBranch>.merge}. + */ + UPSTREAM("tracking"), //$NON-NLS-1$ + + /** + * Like {@link #UPSTREAM}, but only if the upstream name is the same as + * the name of the current local branch. + */ + SIMPLE, + + /** + * Push all current local branches that match a configured push refspec + * of the remote configuration. + */ + MATCHING; + + private final String alias; + + private PushDefault() { + alias = null; + } + + private PushDefault(String alias) { + this.alias = alias; + } + + @Override + public String toConfigValue() { + return name().toLowerCase(Locale.ROOT); + } + + @Override + public boolean matchConfigValue(String in) { + return toConfigValue().equalsIgnoreCase(in) + || (alias != null && alias.equalsIgnoreCase(in)); + } + } + + private final PushRecurseSubmodulesMode recurseSubmodules; + + private final PushDefault pushDefault; + + /** + * Creates a new instance. + * + * @param config + * {@link Config} to fill the {@link PushConfig} from + * @since 6.1 + */ + public PushConfig(Config config) { + recurseSubmodules = config.getEnum(ConfigConstants.CONFIG_PUSH_SECTION, + null, ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES, + PushRecurseSubmodulesMode.NO); + pushDefault = config.getEnum(ConfigConstants.CONFIG_PUSH_SECTION, null, + ConfigConstants.CONFIG_KEY_DEFAULT, PushDefault.SIMPLE); + } + + /** + * Retrieves the value of git config {@code push.recurseSubmodules}. + * + * @return the value + * @since 6.1 + */ + public PushRecurseSubmodulesMode getRecurseSubmodules() { + return recurseSubmodules; + } + + /** + * Retrieves the value of git config {@code push.default}. + * + * @return the value + * @since 6.1 + */ + public PushDefault getPushDefault() { + return pushDefault; + } } 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 a244c55a38..942dad46e0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java @@ -18,11 +18,15 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import org.eclipse.jgit.api.errors.AbortedByHookException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.NotSupportedException; import org.eclipse.jgit.errors.TransportException; +import org.eclipse.jgit.hooks.PrePushHook; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; @@ -58,6 +62,8 @@ class PushProcess { /** A list of option strings associated with this push */ private List<String> pushOptions; + private final PrePushHook prePush; + /** * Create process for specified transport and refs updates specification. * @@ -66,12 +72,14 @@ class PushProcess { * connection. * @param toPush * specification of refs updates (and local tracking branches). - * + * @param prePush + * {@link PrePushHook} to run after the remote advertisement has + * been gotten * @throws TransportException */ - PushProcess(final Transport transport, - final Collection<RemoteRefUpdate> toPush) throws TransportException { - this(transport, toPush, null); + PushProcess(Transport transport, Collection<RemoteRefUpdate> toPush, + PrePushHook prePush) throws TransportException { + this(transport, toPush, prePush, null); } /** @@ -82,16 +90,19 @@ class PushProcess { * connection. * @param toPush * specification of refs updates (and local tracking branches). + * @param prePush + * {@link PrePushHook} to run after the remote advertisement has + * been gotten * @param out * OutputStream to write messages to * @throws TransportException */ - PushProcess(final Transport transport, - final Collection<RemoteRefUpdate> toPush, OutputStream out) - throws TransportException { + PushProcess(Transport transport, Collection<RemoteRefUpdate> toPush, + PrePushHook prePush, OutputStream out) throws TransportException { this.walker = new RevWalk(transport.local); this.transport = transport; this.toPush = new LinkedHashMap<>(); + this.prePush = prePush; this.out = out; this.pushOptions = transport.getPushOptions(); for (RemoteRefUpdate rru : toPush) { @@ -129,10 +140,38 @@ class PushProcess { res.setAdvertisedRefs(transport.getURI(), connection .getRefsMap()); res.peerUserAgent = connection.getPeerUserAgent(); - res.setRemoteUpdates(toPush); monitor.endTask(); + Map<String, RemoteRefUpdate> expanded = expandMatching(); + toPush.clear(); + toPush.putAll(expanded); + + res.setRemoteUpdates(toPush); final Map<String, RemoteRefUpdate> preprocessed = prepareRemoteUpdates(); + List<RemoteRefUpdate> willBeAttempted = preprocessed.values() + .stream().filter(u -> { + switch (u.getStatus()) { + case NON_EXISTING: + case REJECTED_NODELETE: + case REJECTED_NONFASTFORWARD: + case REJECTED_OTHER_REASON: + case REJECTED_REMOTE_CHANGED: + case UP_TO_DATE: + return false; + default: + return true; + } + }).collect(Collectors.toList()); + if (!willBeAttempted.isEmpty()) { + if (prePush != null) { + try { + prePush.setRefs(willBeAttempted); + prePush.call(); + } catch (AbortedByHookException | IOException e) { + throw new TransportException(e.getMessage(), e); + } + } + } if (transport.isDryRun()) modifyUpdatesForDryRun(); else if (!preprocessed.isEmpty()) @@ -201,25 +240,8 @@ class PushProcess { continue; } - // check for fast-forward: - // - both old and new ref must point to commits, AND - // - both of them must be known for us, exist in repository, AND - // - old commit must be ancestor of new commit - boolean fastForward = true; - try { - RevObject oldRev = walker.parseAny(advertisedOld); - final RevObject newRev = walker.parseAny(rru.getNewObjectId()); - if (!(oldRev instanceof RevCommit) - || !(newRev instanceof RevCommit) - || !walker.isMergedInto((RevCommit) oldRev, - (RevCommit) newRev)) - fastForward = false; - } catch (MissingObjectException x) { - fastForward = false; - } catch (Exception x) { - throw new TransportException(transport.getURI(), MessageFormat.format( - JGitText.get().readingObjectsFromLocalRepositoryFailed, x.getMessage()), x); - } + boolean fastForward = isFastForward(advertisedOld, + rru.getNewObjectId()); rru.setFastForward(fastForward); if (!fastForward && !rru.isForceUpdate()) { rru.setStatus(Status.REJECTED_NONFASTFORWARD); @@ -233,6 +255,134 @@ class PushProcess { return result; } + /** + * Determines whether an update from {@code oldOid} to {@code newOid} is a + * fast-forward update: + * <ul> + * <li>both old and new must be commits, AND</li> + * <li>both of them must be known to us and exist in the repository, + * AND</li> + * <li>the old commit must be an ancestor of the new commit.</li> + * </ul> + * + * @param oldOid + * {@link ObjectId}Â of the old commit + * @param newOid + * {@link ObjectId}Â of the new commit + * @return {@code true} if the update fast-forwards, {@code false} otherwise + * @throws TransportException + */ + private boolean isFastForward(ObjectId oldOid, ObjectId newOid) + throws TransportException { + try { + RevObject oldRev = walker.parseAny(oldOid); + RevObject newRev = walker.parseAny(newOid); + if (!(oldRev instanceof RevCommit) || !(newRev instanceof RevCommit) + || !walker.isMergedInto((RevCommit) oldRev, + (RevCommit) newRev)) { + return false; + } + } catch (MissingObjectException x) { + return false; + } catch (Exception x) { + throw new TransportException(transport.getURI(), + MessageFormat.format(JGitText + .get().readingObjectsFromLocalRepositoryFailed, + x.getMessage()), + x); + } + return true; + } + + /** + * Expands all placeholder {@link RemoteRefUpdate}s for "matching" + * {@link RefSpec}s ":" in {@link #toPush} and returns the resulting map in + * which the placeholders have been replaced by their expansion. + * + * @return a new map of {@link RemoteRefUpdate}s keyed by remote name + * @throws TransportException + * if the expansion results in duplicate updates + */ + private Map<String, RemoteRefUpdate> expandMatching() + throws TransportException { + Map<String, RemoteRefUpdate> result = new LinkedHashMap<>(); + boolean hadMatch = false; + for (RemoteRefUpdate update : toPush.values()) { + if (update.isMatching()) { + if (hadMatch) { + throw new TransportException(MessageFormat.format( + JGitText.get().duplicateRemoteRefUpdateIsIllegal, + ":")); //$NON-NLS-1$ + } + expandMatching(result, update); + hadMatch = true; + } else if (result.put(update.getRemoteName(), update) != null) { + throw new TransportException(MessageFormat.format( + JGitText.get().duplicateRemoteRefUpdateIsIllegal, + update.getRemoteName())); + } + } + return result; + } + + /** + * Expands the placeholder {@link RemoteRefUpdate} {@code match} for a + * "matching" {@link RefSpec} ":" or "+:" and puts the expansion into the + * given map {@code updates}. + * + * @param updates + * map to put the expansion in + * @param match + * the placeholder {@link RemoteRefUpdate} to expand + * + * @throws TransportException + * if the expansion results in duplicate updates, or the local + * branches cannot be determined + */ + private void expandMatching(Map<String, RemoteRefUpdate> updates, + RemoteRefUpdate match) throws TransportException { + try { + Map<String, Ref> advertisement = connection.getRefsMap(); + Collection<RefSpec> fetchSpecs = match.getFetchSpecs(); + boolean forceUpdate = match.isForceUpdate(); + for (Ref local : transport.local.getRefDatabase() + .getRefsByPrefix(Constants.R_HEADS)) { + if (local.isSymbolic()) { + continue; + } + String name = local.getName(); + Ref advertised = advertisement.get(name); + if (advertised == null || advertised.isSymbolic()) { + continue; + } + ObjectId oldOid = advertised.getObjectId(); + if (oldOid == null || ObjectId.zeroId().equals(oldOid)) { + continue; + } + ObjectId newOid = local.getObjectId(); + if (newOid == null || ObjectId.zeroId().equals(newOid)) { + continue; + } + + RemoteRefUpdate rru = new RemoteRefUpdate(transport.local, name, + newOid, name, forceUpdate, + Transport.findTrackingRefName(name, fetchSpecs), + oldOid); + if (updates.put(rru.getRemoteName(), rru) != null) { + throw new TransportException(MessageFormat.format( + JGitText.get().duplicateRemoteRefUpdateIsIllegal, + rru.getRemoteName())); + } + } + } catch (IOException x) { + throw new TransportException(transport.getURI(), + MessageFormat.format(JGitText + .get().readingObjectsFromLocalRepositoryFailed, + x.getMessage()), + x); + } + } + private Map<String, RemoteRefUpdate> rejectAll() { for (RemoteRefUpdate rru : toPush.values()) { if (rru.getStatus() == Status.NOT_ATTEMPTED) { 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 ac357afdae..56d0036a20 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java @@ -12,11 +12,11 @@ package org.eclipse.jgit.transport; import java.io.Serializable; import java.text.MessageFormat; +import java.util.Objects; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.util.References; /** * Describes how refs in one repository copy into another repository. @@ -50,6 +50,9 @@ public class RefSpec implements Serializable { /** Is this specification actually a wildcard match? */ private boolean wildcard; + /** Is this the special ":" RefSpec? */ + private boolean matching; + /** * How strict to be about wildcards. * @@ -71,6 +74,7 @@ public class RefSpec implements Serializable { */ ALLOW_MISMATCH } + /** Whether a wildcard is allowed on one side but not the other. */ private WildcardMode allowMismatchedWildcards; @@ -87,6 +91,7 @@ public class RefSpec implements Serializable { * applications, as at least one field must be set to match a source name. */ public RefSpec() { + matching = false; force = false; wildcard = false; srcName = Constants.HEAD; @@ -133,17 +138,25 @@ public class RefSpec implements Serializable { s = s.substring(1); } + boolean matchPushSpec = false; final int c = s.lastIndexOf(':'); if (c == 0) { s = s.substring(1); - if (isWildcard(s)) { + if (s.isEmpty()) { + matchPushSpec = true; wildcard = true; - if (mode == WildcardMode.REQUIRE_MATCH) { - throw new IllegalArgumentException(MessageFormat - .format(JGitText.get().invalidWildcards, spec)); + srcName = Constants.R_HEADS + '*'; + dstName = srcName; + } else { + if (isWildcard(s)) { + wildcard = true; + if (mode == WildcardMode.REQUIRE_MATCH) { + throw new IllegalArgumentException(MessageFormat + .format(JGitText.get().invalidWildcards, spec)); + } } + dstName = checkValid(s); } - dstName = checkValid(s); } else if (c > 0) { String src = s.substring(0, c); String dst = s.substring(c + 1); @@ -168,6 +181,7 @@ public class RefSpec implements Serializable { } srcName = checkValid(s); } + matching = matchPushSpec; } /** @@ -195,6 +209,7 @@ public class RefSpec implements Serializable { } private RefSpec(RefSpec p) { + matching = false; force = p.isForceUpdate(); wildcard = p.isWildcard(); srcName = p.getSource(); @@ -203,6 +218,17 @@ public class RefSpec implements Serializable { } /** + * Tells whether this {@link RefSpec} is the special "matching" RefSpec ":" + * for pushing. + * + * @return whether this is a "matching" RefSpec + * @since 6.1 + */ + public boolean isMatching() { + return matching; + } + + /** * Check if this specification wants to forcefully update the destination. * * @return true if this specification asks for updates without merge tests. @@ -220,6 +246,7 @@ public class RefSpec implements Serializable { */ public RefSpec setForceUpdate(boolean forceUpdate) { final RefSpec r = new RefSpec(this); + r.matching = matching; r.force = forceUpdate; return r; } @@ -322,8 +349,7 @@ public class RefSpec implements Serializable { * The wildcard status of the new source disagrees with the * wildcard status of the new destination. */ - public RefSpec setSourceDestination(final String source, - final String destination) { + public RefSpec setSourceDestination(String source, String destination) { if (isWildcard(source) != isWildcard(destination)) throw new IllegalStateException(JGitText.get().sourceDestinationMustMatch); final RefSpec r = new RefSpec(this); @@ -541,37 +567,36 @@ public class RefSpec implements Serializable { if (!(obj instanceof RefSpec)) return false; final RefSpec b = (RefSpec) obj; - if (isForceUpdate() != b.isForceUpdate()) - return false; - if (isWildcard() != b.isWildcard()) + if (isForceUpdate() != b.isForceUpdate()) { return false; - if (!eq(getSource(), b.getSource())) - return false; - if (!eq(getDestination(), b.getDestination())) - return false; - return true; - } - - private static boolean eq(String a, String b) { - if (References.isSameObject(a, b)) { - return true; } - if (a == null || b == null) + if (isMatching()) { + return b.isMatching(); + } else if (b.isMatching()) { return false; - return a.equals(b); + } + return isWildcard() == b.isWildcard() + && Objects.equals(getSource(), b.getSource()) + && Objects.equals(getDestination(), b.getDestination()); } /** {@inheritDoc} */ @Override public String toString() { final StringBuilder r = new StringBuilder(); - if (isForceUpdate()) + if (isForceUpdate()) { r.append('+'); - if (getSource() != null) - r.append(getSource()); - if (getDestination() != null) { + } + if (isMatching()) { r.append(':'); - r.append(getDestination()); + } else { + if (getSource() != null) { + r.append(getSource()); + } + if (getDestination() != null) { + r.append(':'); + r.append(getDestination()); + } } return r.toString(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java index 43eaac7927..218e62c10a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java @@ -12,7 +12,9 @@ package org.eclipse.jgit.transport; import java.io.IOException; import java.text.MessageFormat; +import java.util.Collection; +import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; @@ -116,6 +118,12 @@ public class RemoteRefUpdate { private RefUpdate localUpdate; /** + * If set, the RemoteRefUpdate is a placeholder for the "matching" RefSpec + * to be expanded after the advertisements have been received in a push. + */ + private Collection<RefSpec> fetchSpecs; + + /** * Construct remote ref update request by providing an update specification. * Object is created with default * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#NOT_ATTEMPTED} @@ -157,9 +165,8 @@ public class RemoteRefUpdate { * @throws java.lang.IllegalArgumentException * if some required parameter was null */ - public RemoteRefUpdate(final Repository localDb, final String srcRef, - final String remoteName, final boolean forceUpdate, - final String localName, final ObjectId expectedOldObjectId) + public RemoteRefUpdate(Repository localDb, String srcRef, String remoteName, + boolean forceUpdate, String localName, ObjectId expectedOldObjectId) throws IOException { this(localDb, srcRef, srcRef != null ? localDb.resolve(srcRef) : ObjectId.zeroId(), remoteName, forceUpdate, localName, @@ -203,9 +210,8 @@ public class RemoteRefUpdate { * @throws java.lang.IllegalArgumentException * if some required parameter was null */ - public RemoteRefUpdate(final Repository localDb, final Ref srcRef, - final String remoteName, final boolean forceUpdate, - final String localName, final ObjectId expectedOldObjectId) + public RemoteRefUpdate(Repository localDb, Ref srcRef, String remoteName, + boolean forceUpdate, String localName, ObjectId expectedOldObjectId) throws IOException { this(localDb, srcRef != null ? srcRef.getName() : null, srcRef != null ? srcRef.getObjectId() : null, remoteName, @@ -255,28 +261,41 @@ public class RemoteRefUpdate { * @throws java.lang.IllegalArgumentException * if some required parameter was null */ - public RemoteRefUpdate(final Repository localDb, final String srcRef, - final ObjectId srcId, final String remoteName, - final boolean forceUpdate, final String localName, - final ObjectId expectedOldObjectId) throws IOException { - if (remoteName == null) - throw new IllegalArgumentException(JGitText.get().remoteNameCannotBeNull); - if (srcId == null && srcRef != null) - throw new IOException(MessageFormat.format( - JGitText.get().sourceRefDoesntResolveToAnyObject, srcRef)); - - if (srcRef != null) + public RemoteRefUpdate(Repository localDb, String srcRef, ObjectId srcId, + String remoteName, boolean forceUpdate, String localName, + ObjectId expectedOldObjectId) throws IOException { + this(localDb, srcRef, srcId, remoteName, forceUpdate, localName, null, + expectedOldObjectId); + } + + private RemoteRefUpdate(Repository localDb, String srcRef, ObjectId srcId, + String remoteName, boolean forceUpdate, String localName, + Collection<RefSpec> fetchSpecs, ObjectId expectedOldObjectId) + throws IOException { + if (fetchSpecs == null) { + if (remoteName == null) { + throw new IllegalArgumentException( + JGitText.get().remoteNameCannotBeNull); + } + if (srcId == null && srcRef != null) { + throw new IOException(MessageFormat.format( + JGitText.get().sourceRefDoesntResolveToAnyObject, + srcRef)); + } + } + if (srcRef != null) { this.srcRef = srcRef; - else if (srcId != null && !srcId.equals(ObjectId.zeroId())) + } else if (srcId != null && !srcId.equals(ObjectId.zeroId())) { this.srcRef = srcId.name(); - else + } else { this.srcRef = null; - - if (srcId != null) + } + if (srcId != null) { this.newObjectId = srcId; - else + } else { this.newObjectId = ObjectId.zeroId(); - + } + this.fetchSpecs = fetchSpecs; this.remoteName = remoteName; this.forceUpdate = forceUpdate; if (localName != null && localDb != null) { @@ -292,8 +311,9 @@ public class RemoteRefUpdate { ? localUpdate.getOldObjectId() : ObjectId.zeroId(), newObjectId); - } else + } else { trackingRefUpdate = null; + } this.localDb = localDb; this.expectedOldObjectId = expectedOldObjectId; this.status = Status.NOT_ATTEMPTED; @@ -316,11 +336,57 @@ public class RemoteRefUpdate { * local tracking branch or srcRef of base object no longer can * be resolved to any object. */ - public RemoteRefUpdate(final RemoteRefUpdate base, - final ObjectId newExpectedOldObjectId) throws IOException { - this(base.localDb, base.srcRef, base.remoteName, base.forceUpdate, + public RemoteRefUpdate(RemoteRefUpdate base, + ObjectId newExpectedOldObjectId) throws IOException { + this(base.localDb, base.srcRef, base.newObjectId, base.remoteName, + base.forceUpdate, (base.trackingRefUpdate == null ? null : base.trackingRefUpdate - .getLocalName()), newExpectedOldObjectId); + .getLocalName()), + base.fetchSpecs, newExpectedOldObjectId); + } + + /** + * Creates a "placeholder" update for the "matching" RefSpec ":". + * + * @param localDb + * local repository to push from + * @param forceUpdate + * whether non-fast-forward updates shall be allowed + * @param fetchSpecs + * The fetch {@link RefSpec}s to use when this placeholder is + * expanded to determine remote tracking branch updates + */ + RemoteRefUpdate(Repository localDb, boolean forceUpdate, + @NonNull Collection<RefSpec> fetchSpecs) { + this.localDb = localDb; + this.forceUpdate = forceUpdate; + this.fetchSpecs = fetchSpecs; + this.trackingRefUpdate = null; + this.srcRef = null; + this.remoteName = null; + this.newObjectId = null; + this.status = Status.NOT_ATTEMPTED; + } + + /** + * Tells whether this {@link RemoteRefUpdate} is a placeholder for a + * "matching" {@link RefSpec}. + * + * @return {@code true} if this is a placeholder, {@code false} otherwise + * @since 6.1 + */ + public boolean isMatching() { + return fetchSpecs != null; + } + + /** + * Retrieves the fetch {@link RefSpec}s of this {@link RemoteRefUpdate}. + * + * @return the fetch {@link RefSpec}s, or {@code null} if + * {@code this.}{@link #isMatching()} {@code == false} + */ + Collection<RefSpec> getFetchSpecs() { + return fetchSpecs; } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java index 212a4e46c1..48cacf0964 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2018, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -44,6 +44,14 @@ public final class SshConstants { // Config file keys + /** + * Property to control whether private keys are added to an SSH agent, if + * one is running, after having been loaded. + * + * @since 6.1 + */ + public static final String ADD_KEYS_TO_AGENT = "AddKeysToAgent"; + /** Key in an ssh config file. */ public static final String BATCH_MODE = "BatchMode"; @@ -62,6 +70,15 @@ public final class SshConstants { /** Key in an ssh config file. */ public static final String CONNECTION_ATTEMPTS = "ConnectionAttempts"; + /** + * An OpenSSH time value for the connection timeout. In OpenSSH, this + * includes everything until the end of the initial key exchange; in JGit it + * covers only the underlying TCP connect. + * + * @since 6.1 + */ + public static final String CONNECT_TIMEOUT = "ConnectTimeout"; + /** Key in an ssh config file. */ public static final String CONTROL_PATH = "ControlPath"; @@ -159,6 +176,14 @@ public final class SshConstants { /** Key in an ssh config file. */ public static final String REMOTE_FORWARD = "RemoteForward"; + /** + * (Absolute) path to a middleware library the SSH agent shall use to load + * SK (U2F) keys. + * + * @since 6.1 + */ + public static final String SECURITY_KEY_PROVIDER = "SecurityKeyProvider"; + /** Key in an ssh config file. */ public static final String SEND_ENV = "SendEnv"; @@ -229,4 +254,12 @@ public final class SshConstants { public static final String[] DEFAULT_IDENTITIES = { // ID_RSA, ID_DSA, ID_ECDSA, ID_ED25519 }; + + /** + * Name of the environment variable holding the Unix domain socket for + * communication with an SSH agent. + * + * @since 6.1 + */ + public static final String ENV_SSH_AUTH_SOCKET = "SSH_AUTH_SOCK"; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java index 696ca7cf46..51bc07cb94 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java @@ -12,6 +12,8 @@ package org.eclipse.jgit.transport; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH; + import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.RefUpdate; @@ -184,9 +186,13 @@ public class TrackingRefUpdate { if (forceUpdate) sb.append(" (forced)"); sb.append(" "); - sb.append(oldObjectId == null ? "" : oldObjectId.abbreviate(7).name()); + sb.append(oldObjectId == null ? "" + : oldObjectId.abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH) + .name()); sb.append(".."); - sb.append(newObjectId == null ? "" : newObjectId.abbreviate(7).name()); + sb.append(newObjectId == null ? "" + : newObjectId.abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH) + .name()); sb.append("]"); return sb.toString(); } 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 5b781ac25f..0eab4434ed 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java @@ -2,7 +2,7 @@ * Copyright (C) 2008, 2009 Google Inc. * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> - * Copyright (C) 2008, 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 @@ -40,7 +40,6 @@ import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.Nullable; -import org.eclipse.jgit.api.errors.AbortedByHookException; import org.eclipse.jgit.errors.NotSupportedException; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.hooks.Hooks; @@ -590,6 +589,11 @@ public abstract class Transport implements AutoCloseable { final Collection<RefSpec> procRefs = expandPushWildcardsFor(db, specs); for (RefSpec spec : procRefs) { + if (spec.isMatching()) { + result.add(new RemoteRefUpdate(db, spec.isForceUpdate(), + fetchSpecs)); + continue; + } String srcSpec = spec.getSource(); final Ref srcRef = db.findRef(srcSpec); if (srcRef != null) @@ -656,14 +660,18 @@ public abstract class Transport implements AutoCloseable { private static Collection<RefSpec> expandPushWildcardsFor( final Repository db, final Collection<RefSpec> specs) throws IOException { - final List<Ref> localRefs = db.getRefDatabase().getRefs(); final Collection<RefSpec> procRefs = new LinkedHashSet<>(); + List<Ref> localRefs = null; for (RefSpec spec : specs) { - if (spec.isWildcard()) { + if (!spec.isMatching() && spec.isWildcard()) { + if (localRefs == null) { + localRefs = db.getRefDatabase().getRefs(); + } for (Ref localRef : localRefs) { - if (spec.matchSource(localRef)) + if (spec.matchSource(localRef)) { procRefs.add(spec.expandFromSource(localRef)); + } } } else { procRefs.add(spec); @@ -672,7 +680,7 @@ public abstract class Transport implements AutoCloseable { return procRefs; } - private static String findTrackingRefName(final String remoteName, + static String findTrackingRefName(final String remoteName, final Collection<RefSpec> fetchSpecs) { // try to find matching tracking refs for (RefSpec fetchSpec : fetchSpecs) { @@ -1371,16 +1379,9 @@ public abstract class Transport implements AutoCloseable { if (toPush.isEmpty()) throw new TransportException(JGitText.get().nothingToPush); } - if (prePush != null) { - try { - prePush.setRefs(toPush); - prePush.call(); - } catch (AbortedByHookException | IOException e) { - throw new TransportException(e.getMessage(), e); - } - } - final PushProcess pushProcess = new PushProcess(this, toPush, out); + final PushProcess pushProcess = new PushProcess(this, toPush, prePush, + out); return pushProcess.execute(monitor); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java index 0c23c8cadf..f9b5eafa93 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -1933,10 +1933,9 @@ public class UploadPack implements Closeable { @Override public void checkWants(UploadPack up, List<ObjectId> wants) throws PackProtocolException, IOException { - if (!up.isBiDirectionalPipe()) + if (!up.isBiDirectionalPipe() || !wants.isEmpty()) { new ReachableCommitRequestValidator().checkWants(up, wants); - else if (!wants.isEmpty()) - throw new WantNotValidException(wants.iterator().next()); + } } } @@ -2358,7 +2357,7 @@ public class UploadPack implements Closeable { : req.getDepth() - 1; pw.setShallowPack(req.getDepth(), unshallowCommits); - @SuppressWarnings("resource") // Ownership is transferred below + // Ownership is transferred below DepthWalk.RevWalk dw = new DepthWalk.RevWalk( walk.getObjectReader(), walkDepth); dw.setDeepenSince(req.getDeepenSince()); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java index 3d15ef5e72..046f395049 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2010, Google Inc. and others + * Copyright (C) 2009-2022, 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 @@ -18,11 +18,11 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.jgit.errors.RepositoryNotFoundException; -import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryCache; import org.eclipse.jgit.lib.RepositoryCache.FileKey; import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.StringUtils; /** * Default resolver serving from the local filesystem. @@ -67,7 +67,7 @@ public class FileResolver<C> implements RepositoryResolver<C> { if (isUnreasonableName(name)) throw new RepositoryNotFoundException(name); - Repository db = exports.get(nameWithDotGit(name)); + Repository db = exports.get(StringUtils.nameWithDotGit(name)); if (db != null) { db.incrementOpen(); return db; @@ -154,7 +154,7 @@ public class FileResolver<C> implements RepositoryResolver<C> { * the repository instance. */ public void exportRepository(String name, Repository db) { - exports.put(nameWithDotGit(name), db); + exports.put(StringUtils.nameWithDotGit(name), db); } /** @@ -197,12 +197,6 @@ public class FileResolver<C> implements RepositoryResolver<C> { return false; } - private static String nameWithDotGit(String name) { - if (name.endsWith(Constants.DOT_GIT_EXT)) - return name; - return name + Constants.DOT_GIT_EXT; - } - private static boolean isUnreasonableName(String name) { if (name.length() == 0) return true; // no empty paths diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java index 1f614e31f6..8269666d26 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java @@ -1,6 +1,6 @@ /* - * Copyright (C) 2008-2009, Google Inc. - * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others + * Copyright (C) 2008, 2009 Google Inc. + * Copyright (C) 2008, 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 @@ -14,6 +14,7 @@ package org.eclipse.jgit.treewalk; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -73,6 +74,7 @@ import org.eclipse.jgit.util.io.EolStreamTypeUtil; * threads. */ public class TreeWalk implements AutoCloseable, AttributesProvider { + private static final AbstractTreeIterator[] NO_TREES = {}; /** @@ -92,7 +94,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { } /** - * Type of operation you want to retrieve the git attributes for. + * Type of operation you want to retrieve the git attributes for. */ private OperationType operationType = OperationType.CHECKOUT_OP; @@ -284,11 +286,20 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { AbstractTreeIterator currentHead; - /** Cached attribute for the current entry */ - private Attributes attrs = null; + /** + * Cached attributes for the current entry; per tree. Index i+1 is for tree + * i; index 0 is for the deprecated legacy behavior. + */ + private Attributes[] attrs; + + /** + * Cached attributes handler; per tree. Index i+1 is for tree i; index 0 is + * for the deprecated legacy behavior. + */ + private AttributesHandler[] attributesHandlers; - /** Cached attributes handler */ - private AttributesHandler attributesHandler; + /** Can be set to identify the tree to use for {@link #getAttributes()}. */ + private int headIndex = -1; private Config config; @@ -515,6 +526,24 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { } /** + * Identifies the tree at the given index as the head tree. This is the tree + * use by default to determine attributes and EOL modes. + * + * @param index + * of the tree to use as head + * @throws IllegalArgumentException + * if the index is out of range + * @since 6.1 + */ + public void setHead(int index) { + if (index < 0 || index >= trees.length) { + throw new IllegalArgumentException("Head index " + index //$NON-NLS-1$ + + " out of range [0," + trees.length + ')'); //$NON-NLS-1$ + } + headIndex = index; + } + + /** * {@inheritDoc} * <p> * Retrieve the git attributes for the current entry. @@ -556,25 +585,51 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { */ @Override public Attributes getAttributes() { - if (attrs != null) - return attrs; + return getAttributes(headIndex); + } + /** + * Retrieves the git attributes based on the given tree. + * + * @param index + * of the tree to use as base for the attributes + * @return the attributes + * @since 6.1 + */ + public Attributes getAttributes(int index) { + int attrIndex = index + 1; + Attributes result = attrs[attrIndex]; + if (result != null) { + return result; + } if (attributesNodeProvider == null) { - // The work tree should have a AttributesNodeProvider to be able to - // retrieve the info and global attributes node throw new IllegalStateException( "The tree walk should have one AttributesNodeProvider set in order to compute the git attributes."); //$NON-NLS-1$ } try { - // Lazy create the attributesHandler on the first access of - // attributes. This requires the info, global and root - // attributes nodes - if (attributesHandler == null) { - attributesHandler = new AttributesHandler(this); + AttributesHandler handler = attributesHandlers[attrIndex]; + if (handler == null) { + if (index < 0) { + // Legacy behavior (headIndex not set, getAttributes() above + // called) + handler = new AttributesHandler(this, () -> { + return getTree(CanonicalTreeParser.class); + }); + } else { + handler = new AttributesHandler(this, () -> { + AbstractTreeIterator tree = trees[index]; + if (tree instanceof CanonicalTreeParser) { + return (CanonicalTreeParser) tree; + } + return null; + }); + } + attributesHandlers[attrIndex] = handler; } - attrs = attributesHandler.getAttributes(); - return attrs; + result = handler.getAttributes(); + attrs[attrIndex] = result; + return result; } catch (IOException e) { throw new JGitInternalException("Error while parsing attributes", //$NON-NLS-1$ e); @@ -595,11 +650,34 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { */ @Nullable public EolStreamType getEolStreamType(OperationType opType) { - if (attributesNodeProvider == null || config == null) + if (attributesNodeProvider == null || config == null) { return null; - return EolStreamTypeUtil.detectStreamType( - opType != null ? opType : operationType, - config.get(WorkingTreeOptions.KEY), getAttributes()); + } + OperationType op = opType != null ? opType : operationType; + return EolStreamTypeUtil.detectStreamType(op, + config.get(WorkingTreeOptions.KEY), getAttributes()); + } + + /** + * Get the EOL stream type of the current entry for checking out using the + * config and {@link #getAttributes()}. + * + * @param tree + * index of the tree the check-out is to be from + * @return the EOL stream type of the current entry using the config and + * {@link #getAttributes()}. Note that this method may return null + * if the {@link org.eclipse.jgit.treewalk.TreeWalk} is not based on + * a working tree + * @since 6.1 + */ + @Nullable + public EolStreamType getCheckoutEolStreamType(int tree) { + if (attributesNodeProvider == null || config == null) { + return null; + } + Attributes attr = getAttributes(tree); + return EolStreamTypeUtil.detectStreamType(OperationType.CHECKOUT_OP, + config.get(WorkingTreeOptions.KEY), attr); } /** @@ -607,7 +685,8 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { */ public void reset() { attrs = null; - attributesHandler = null; + attributesHandlers = null; + headIndex = -1; trees = NO_TREES; advance = false; depth = 0; @@ -651,7 +730,9 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { advance = false; depth = 0; - attrs = null; + attrs = new Attributes[2]; + attributesHandlers = new AttributesHandler[2]; + headIndex = -1; } /** @@ -701,7 +782,14 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { trees = r; advance = false; depth = 0; - attrs = null; + if (oldLen == newLen) { + Arrays.fill(attrs, null); + Arrays.fill(attributesHandlers, null); + } else { + attrs = new Attributes[newLen + 1]; + attributesHandlers = new AttributesHandler[newLen + 1]; + } + headIndex = -1; } /** @@ -758,6 +846,16 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { p.matchShift = 0; trees = newTrees; + if (attrs == null) { + attrs = new Attributes[n + 2]; + } else { + attrs = Arrays.copyOf(attrs, n + 2); + } + if (attributesHandlers == null) { + attributesHandlers = new AttributesHandler[n + 2]; + } else { + attributesHandlers = Arrays.copyOf(attributesHandlers, n + 2); + } return n; } @@ -800,7 +898,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { } for (;;) { - attrs = null; + Arrays.fill(attrs, null); final AbstractTreeIterator t = min(); if (t.eof()) { if (depth > 0) { @@ -1255,7 +1353,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { */ public void enterSubtree() throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException { - attrs = null; + Arrays.fill(attrs, null); final AbstractTreeIterator ch = currentHead; final AbstractTreeIterator[] tmp = new AbstractTreeIterator[trees.length]; for (int i = 0; i < trees.length; i++) { @@ -1374,11 +1472,12 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { /** * Inspect config and attributes to return a filtercommand applicable for - * the current path, but without expanding %f occurences + * the current path. * * @param filterCommandType * which type of filterCommand should be executed. E.g. "clean", - * "smudge" + * "smudge". For "smudge" consider using + * {{@link #getSmudgeCommand(int)} instead. * @return a filter command * @throws java.io.IOException * @since 4.2 @@ -1407,6 +1506,54 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { } /** + * Inspect config and attributes to return a filtercommand applicable for + * the current path. + * + * @param index + * of the tree the item to be smudged is in + * @return a filter command + * @throws java.io.IOException + * @since 6.1 + */ + public String getSmudgeCommand(int index) + throws IOException { + return getSmudgeCommand(getAttributes(index)); + } + + /** + * Inspect config and attributes to return a filtercommand applicable for + * the current path. + * + * @param attributes + * to use + * @return a filter command + * @throws java.io.IOException + * @since 6.1 + */ + public String getSmudgeCommand(Attributes attributes) throws IOException { + if (attributes == null) { + return null; + } + Attribute f = attributes.get(Constants.ATTR_FILTER); + if (f == null) { + return null; + } + String filterValue = f.getValue(); + if (filterValue == null) { + return null; + } + + String filterCommand = getFilterCommandDefinition(filterValue, + Constants.ATTR_FILTER_TYPE_SMUDGE); + if (filterCommand == null) { + return null; + } + return filterCommand.replaceAll("%f", //$NON-NLS-1$ + Matcher.quoteReplacement( + QuotedString.BOURNE.quote((getPathString())))); + } + + /** * Get the filter command how it is defined in gitconfig. The returned * string may contain "%f" which needs to be replaced by the current path * before executing the filter command. These filter definitions are cached diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index 50ce15ebc9..427eac5b53 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -2,7 +2,7 @@ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> * Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com> - * Copyright (C) 2012-2021, Robin Rosenberg and others + * Copyright (C) 2012, 2022, Robin Rosenberg and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -387,8 +387,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { state.initializeReadBuffer(); final long len = e.getLength(); - InputStream filteredIs = possiblyFilteredInputStream(e, is, len, - OperationType.CHECKIN_OP); + InputStream filteredIs = possiblyFilteredInputStream(e, is, + len); return computeHash(filteredIs, canonLen); } finally { safeClose(is); @@ -400,23 +400,18 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } private InputStream possiblyFilteredInputStream(final Entry e, - final InputStream is, final long len) throws IOException { - return possiblyFilteredInputStream(e, is, len, null); - - } - - private InputStream possiblyFilteredInputStream(final Entry e, - final InputStream is, final long len, OperationType opType) + final InputStream is, final long len) throws IOException { if (getCleanFilterCommand() == null - && getEolStreamType(opType) == EolStreamType.DIRECT) { + && getEolStreamType( + OperationType.CHECKIN_OP) == EolStreamType.DIRECT) { canonLen = len; return is; } if (len <= MAXIMUM_FILE_SIZE_TO_READ_FULLY) { ByteBuffer rawbuf = IO.readWholeStream(is, (int) len); - rawbuf = filterClean(rawbuf.array(), rawbuf.limit(), opType); + rawbuf = filterClean(rawbuf.array(), rawbuf.limit()); canonLen = rawbuf.limit(); return new ByteArrayInputStream(rawbuf.array(), 0, (int) canonLen); } @@ -426,14 +421,13 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { return is; } - final InputStream lenIs = filterClean(e.openInputStream(), - opType); + final InputStream lenIs = filterClean(e.openInputStream()); try { canonLen = computeLength(lenIs); } finally { safeClose(lenIs); } - return filterClean(is, opType); + return filterClean(is); } private static void safeClose(InputStream in) { @@ -455,23 +449,20 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } } - private ByteBuffer filterClean(byte[] src, int n, OperationType opType) + private ByteBuffer filterClean(byte[] src, int n) throws IOException { InputStream in = new ByteArrayInputStream(src); try { - return IO.readWholeStream(filterClean(in, opType), n); + return IO.readWholeStream(filterClean(in), n); } finally { safeClose(in); } } - private InputStream filterClean(InputStream in) throws IOException { - return filterClean(in, null); - } - - private InputStream filterClean(InputStream in, OperationType opType) + private InputStream filterClean(InputStream in) throws IOException { - in = handleAutoCRLF(in, opType); + in = EolStreamTypeUtil.wrapInputStream(in, + getEolStreamType(OperationType.CHECKIN_OP)); String filterCommand = getCleanFilterCommand(); if (filterCommand != null) { if (FilterCommandRegistry.isRegistered(filterCommand)) { @@ -509,11 +500,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { return in; } - private InputStream handleAutoCRLF(InputStream in, OperationType opType) - throws IOException { - return EolStreamTypeUtil.wrapInputStream(in, getEolStreamType(opType)); - } - /** * Returns the working tree options used by this iterator. * @@ -664,7 +650,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { public InputStream openEntryStream() throws IOException { InputStream rawis = current().openInputStream(); if (getCleanFilterCommand() == null - && getEolStreamType() == EolStreamType.DIRECT) { + && getEolStreamType( + OperationType.CHECKIN_OP) == EolStreamType.DIRECT) { return rawis; } return filterClean(rawis); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java index ff094f6975..ae73d3feb8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java @@ -16,6 +16,7 @@ import java.io.IOException; import java.nio.file.FileVisitOption; import java.nio.file.FileVisitResult; import java.nio.file.Files; +import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; @@ -96,6 +97,9 @@ public class FS_Win32 extends FS { /** {@inheritDoc} */ @Override public Entry[] list(File directory, FileModeStrategy fileModeStrategy) { + if (!Files.isDirectory(directory.toPath(), LinkOption.NOFOLLOW_LINKS)) { + return NO_ENTRIES; + } List<Entry> result = new ArrayList<>(); FS fs = this; boolean checkExecutable = fs.supportsExecute(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java index 8ab13385e0..917add3609 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2010, Google Inc. and others + * Copyright (C) 2009-2022, 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 @@ -15,6 +15,7 @@ import java.util.Collection; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.Constants; /** * Miscellaneous string comparison utility methods. @@ -37,6 +38,10 @@ public final class StringUtils { LC[c] = (char) ('a' + (c - 'A')); } + private StringUtils() { + // Do not create instances + } + /** * Convert the input to lowercase. * <p> @@ -269,8 +274,20 @@ public final class StringUtils { return sb.toString(); } - private StringUtils() { - // Do not create instances + /** + * Appends {@link Constants#DOT_GIT_EXT} unless the given name already ends + * with that suffix. + * + * @param name + * to complete + * @return the name ending with {@link Constants#DOT_GIT_EXT} + * @since 6.1 + */ + public static String nameWithDotGit(String name) { + if (name.endsWith(Constants.DOT_GIT_EXT)) { + return name; + } + return name + Constants.DOT_GIT_EXT; } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/BinaryHunkInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/BinaryHunkInputStream.java index 4f940d77a0..2c972b578d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/BinaryHunkInputStream.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/BinaryHunkInputStream.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 @@ -90,7 +90,7 @@ public class BinaryHunkInputStream extends InputStream { byte[] encoded = new byte[Base85.encodedLength(length)]; for (int i = 0; i < encoded.length; i++) { int b = in.read(); - if (b < 0 || b == '\n') { + if (b < 0 || b == '\r' || b == '\n') { throw new EOFException(MessageFormat.format( JGitText.get().binaryHunkInvalidLength, Integer.valueOf(lineNumber))); @@ -99,6 +99,10 @@ public class BinaryHunkInputStream extends InputStream { } // Must be followed by a newline; tolerate EOF. int b = in.read(); + if (b == '\r') { + // Be lenient and accept CR-LF, too. + b = in.read(); + } if (b >= 0 && b != '\n') { throw new StreamCorruptedException(MessageFormat.format( JGitText.get().binaryHunkMissingNewline, @@ -18,7 +18,7 @@ <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> <packaging>pom</packaging> - <version>6.0.1-SNAPSHOT</version> + <version>6.1.1-SNAPSHOT</version> <name>JGit - Parent</name> <url>${jgit-url}</url> @@ -150,9 +150,9 @@ <java.version>11</java.version> <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest> - <jgit-last-release-version>5.13.0.202109080827-r</jgit-last-release-version> + <jgit-last-release-version>6.0.0.202111291000-r</jgit-last-release-version> <ant-version>1.10.12</ant-version> - <apache-sshd-version>2.7.0</apache-sshd-version> + <apache-sshd-version>2.8.0</apache-sshd-version> <jsch-version>0.1.55</jsch-version> <jzlib-version>1.1.1</jzlib-version> <javaewah-version>1.1.13</javaewah-version> @@ -160,7 +160,7 @@ <test-fork-count>1C</test-fork-count> <args4j-version>2.33</args4j-version> <commons-compress-version>1.21</commons-compress-version> - <osgi-core-version>4.3.1</osgi-core-version> + <osgi-core-version>6.0.0</osgi-core-version> <servlet-api-version>4.0.0</servlet-api-version> <jetty-version>10.0.6</jetty-version> <japicmp-version>0.15.3</japicmp-version> @@ -168,9 +168,8 @@ <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> - <gson-version>2.8.8</gson-version> - <bouncycastle-version>1.69</bouncycastle-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> <maven-project-info-reports-plugin-version>3.1.2</maven-project-info-reports-plugin-version> <maven-jxr-plugin-version>3.1.1</maven-jxr-plugin-version> @@ -185,6 +184,10 @@ <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin> <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis> <sonar.jacoco.reportPath>${project.build.directory}/jacoco.exec</sonar.jacoco.reportPath> + + <!-- license check --> + <dash.fail>true</dash.fail> + <dash.projectId>technology.jgit</dash.projectId> </properties> <repositories> @@ -200,8 +203,8 @@ <url>https://repo.eclipse.org/content/repositories/cbi-releases/</url> </pluginRepository> <pluginRepository> - <id>repo.eclipse.org.cbi-snapshots</id> - <url>https://repo.eclipse.org/content/repositories/cbi-snapshots/</url> + <id>repo.eclipse.org.dash-releases</id> + <url>https://repo.eclipse.org/content/repositories/dash-licenses-releases/</url> </pluginRepository> </pluginRepositories> @@ -338,7 +341,7 @@ <dependency><!-- add support for ssh/scp --> <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-ssh</artifactId> - <version>3.4.3</version> + <version>3.5.1</version> </dependency> </dependencies> </plugin> @@ -382,6 +385,11 @@ <artifactId>spring-boot-maven-plugin</artifactId> <version>2.5.4</version> </plugin> + <plugin> + <groupId>org.eclipse.dash</groupId> + <artifactId>license-tool-plugin</artifactId> + <version>1.1.0</version> + </plugin> </plugins> </pluginManagement> @@ -404,6 +412,19 @@ </rules> </configuration> </execution> + <execution> + <id>enforce-java</id> + <goals> + <goal>enforce</goal> + </goals> + <configuration> + <rules> + <requireJavaVersion> + <version>17</version> + </requireJavaVersion> + </rules> + </configuration> + </execution> </executions> </plugin> @@ -540,6 +561,18 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-report-plugin</artifactId> </plugin> + <plugin> + <groupId>org.eclipse.dash</groupId> + <artifactId>license-tool-plugin</artifactId> + <executions> + <execution> + <id>license-check</id> + <goals> + <goal>license-check</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build> @@ -853,7 +886,7 @@ <dependency> <groupId>org.eclipse.jdt</groupId> <artifactId>ecj</artifactId> - <version>3.27.0</version> + <version>3.28.0</version> </dependency> </dependencies> </plugin> diff --git a/tools/BUILD b/tools/BUILD index 1826249357..1e671087d6 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -1,34 +1,28 @@ load( "@bazel_tools//tools/jdk:default_java_toolchain.bzl", - "JDK9_JVM_OPTS", "default_java_toolchain", ) load("@rules_java//java:defs.bzl", "java_package_configuration") -JDK11_JVM_OPTS = [ - "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", - "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", - "--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", - "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", - "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", - "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", - "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", - "--patch-module=java.compiler=$(location @bazel_tools//tools/jdk:java_compiler_jar)", - "--patch-module=jdk.compiler=$(location @bazel_tools//tools/jdk:jdk_compiler_jar)", - "--add-opens=java.base/java.nio=ALL-UNNAMED", - "--add-opens=java.base/java.lang=ALL-UNNAMED", -] - default_java_toolchain( - name = "error_prone_warnings_toolchain", - bootclasspath = ["@bazel_tools//tools/jdk:platformclasspath.jar"], - jvm_opts = JDK11_JVM_OPTS, + name = "error_prone_warnings_toolchain_java11", + package_configuration = [ + ":error_prone", + ], source_version = "11", target_version = "11", + visibility = ["//visibility:public"], +) + +default_java_toolchain( + name = "error_prone_warnings_toolchain_java17", + configuration = dict(), + java_runtime = "@bazel_tools//tools/jdk:remotejdk_17", package_configuration = [ ":error_prone", ], + source_version = "17", + target_version = "17", visibility = ["//visibility:public"], ) @@ -108,20 +102,25 @@ package_group( "//org.eclipse.jgit.ant.test/...", "//org.eclipse.jgit.ant/...", "//org.eclipse.jgit.archive/...", + "//org.eclipse.jgit.gpg.bc.test/...", + "//org.eclipse.jgit.gpg.bc/...", "//org.eclipse.jgit.http.apache/...", "//org.eclipse.jgit.http.server/...", "//org.eclipse.jgit.http.test/...", - "//org.eclipse.jgit.junit.http/...", + "//org.eclipse.jgit.junit.ssh/...", "//org.eclipse.jgit.junit/...", + "//org.eclipse.jgit.junit/http/...", "//org.eclipse.jgit.lfs.server.test/...", "//org.eclipse.jgit.lfs.server/...", "//org.eclipse.jgit.lfs.test/...", "//org.eclipse.jgit.lfs/...", - "//org.eclipse.jgit.packaging/...", "//org.eclipse.jgit.pgm.test/...", "//org.eclipse.jgit.pgm/...", - "//org.eclipse.jgit.ssh.apache/...", "//org.eclipse.jgit.ssh.apache.agent/...", + "//org.eclipse.jgit.ssh.apache.test/...", + "//org.eclipse.jgit.ssh.apache/...", + "//org.eclipse.jgit.ssh.jsch.test/...", + "//org.eclipse.jgit.ssh.jsch/...", "//org.eclipse.jgit.test/...", "//org.eclipse.jgit.ui/...", "//org.eclipse.jgit/...", diff --git a/tools/bazelisk_version.bzl b/tools/bazelisk_version.bzl deleted file mode 100644 index d8b3d10982..0000000000 --- a/tools/bazelisk_version.bzl +++ /dev/null @@ -1,16 +0,0 @@ -_template = """ -load("@bazel_skylib//lib:versions.bzl", "versions") - -def check_bazel_version(): - versions.check(minimum_bazel_version = "{version}") -""".strip() - -def _impl(repository_ctx): - repository_ctx.symlink(Label("@//:.bazelversion"), ".bazelversion") - bazelversion = repository_ctx.read(".bazelversion").strip() - - repository_ctx.file("BUILD", executable = False) - - repository_ctx.file("check.bzl", executable = False, content = _template.format(version = bazelversion)) - -bazelisk_version = repository_rule(implementation = _impl) diff --git a/tools/remote-bazelrc b/tools/remote-bazelrc new file mode 100644 index 0000000000..58f794e18b --- /dev/null +++ b/tools/remote-bazelrc @@ -0,0 +1,67 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file is auto-generated from release/bazelrc.tpl and should not be +# modified directly. + +# This .bazelrc file contains all of the flags required for the provided +# toolchain with Remote Build Execution. +# +# This .bazelrc file also contains all of the flags required for the local +# docker sandboxing. + +# Depending on how many machines are in the remote execution instance, setting +# this higher can make builds faster by allowing more jobs to run in parallel. +# Setting it too high can result in jobs that timeout, however, while waiting +# for a remote machine to execute them. +build:remote --jobs=200 +build:remote --disk_cache= + +# Set several flags related to specifying the platform, toolchain and java +# properties. +build:remote --host_javabase=@rbe_jdk11//java:jdk +build:remote --javabase=@rbe_jdk11//java:jdk +build:remote --crosstool_top=@rbe_jdk11//cc:toolchain +build:remote --extra_toolchains=@rbe_jdk11//config:cc-toolchain +build:remote --extra_execution_platforms=@rbe_jdk11//config:platform +build:remote --host_platform=@rbe_jdk11//config:platform +build:remote --platforms=@rbe_jdk11//config:platform +build:remote --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 + +# Set various strategies so that all actions execute remotely. Mixing remote +# and local execution will lead to errors unless the toolchain and remote +# machine exactly match the host machine. +build:remote --define=EXECUTOR=remote + +# Enable the remote cache so action results can be shared across machines, +# developers, and workspaces. +build:remote --remote_cache=remotebuildexecution.googleapis.com + +# Enable remote execution so actions are performed on the remote systems. +build:remote --remote_executor=remotebuildexecution.googleapis.com + +# Set a higher timeout value, just in case. +build:remote --remote_timeout=3600 + +# Enable authentication. This will pick up application default credentials by +# default. You can use --auth_credentials=some_file.json to use a service +# account credential instead. +build:remote --google_default_credentials + +# The following flags enable the remote cache so action results can be shared +# across machines, developers, and workspaces. +build:remote-cache --remote_cache=remotebuildexecution.googleapis.com +build:remote-cache --tls_enabled=true +build:remote-cache --remote_timeout=3600 +build:remote-cache --auth_enabled=true |